Skip to content

feat(packs): add candidate openai 4 to 7 migration pack - #130

Open
lucasbraum wants to merge 2 commits into
mainfrom
claude/keen-brahmagupta-qbatc4
Open

lucasbraum wants to merge 2 commits into
mainfrom
claude/keen-brahmagupta-qbatc4

Conversation

@lucasbraum

Copy link
Copy Markdown
Collaborator

Pack

openai >=4 <5 → >=7 <8 (queue entry openai). Candidate, not verified. Kept as a draft: the gate fails on two engine gaps found while scoring (below), not on the pack's rules. The queue entry is set to blocked with that reason.

One pack covers 4 → 7, not three. 5.0 carries nearly all API changes (MIGRATION.md). 6.0's only breaking entry is one type widening; 7.0's only breaking entry is the Node 22 floor. 5.12.1 (a minor) made tool calls a function | custom union. Nothing later reverts 5.0, and every public upgrade found jumps 4.x → 7.x directly. Evidence: packages/core/src/packs/openai/RESEARCH.md, "One pack, not three".

Sources

Pinned at v7.32.0, read 2026-10-14 (all in meta.sources):

  • MIGRATION.md v7.32.0, compared with its v5.0.0, v6.0.0 and v7.0.0 copies
  • CHANGELOG.md v7.32.0, every entry from 4.104.0 to 7.32.0
  • Release notes for 5.12.1, 6.0.0 and 7.0.0
  • The published package.json and .d.ts of 4.104.0, 5.0.0, 5.11.0, 5.12.0, 5.12.1, 6.0.0, 6.49.0, 7.0.0 and 7.32.0

Each change is classified as mechanical, assisted, behavior note, or not compiler-visible in RESEARCH.md.

Rules and behavior notes

Rules:

  • named-path-params (M): runs.retrieve(threadId, runId) → runs.retrieve(runId, { thread_id }) for 28 methods, which includes 3 the guide's list omits (runs.poll, submitToolOutputsAndPoll, submitToolOutputsStream; taken from the 7.x .d.ts). It rewrites only inert arguments and an unambiguous third argument; anything else goes to the agent. It also has a syntactic detect, so clients typed any are found.
  • delete-method (M): .del() → .delete(). A bare files.del / models.del is detected only on a known client binding.
  • beta-chat (M): beta.chat.completions.parse|stream|runTools → chat.completions.*.
  • runner-renames (M): functionCall* events and finalFunctionCall*() → functionToolCall*.
  • removed-modules (M/A): deletes a side-effect openai/shims/* import; moves beta chat types to openai/resources/chat/completions when every imported name exists there; openai/src → openai. Everything else (openai/core, deeper src paths) goes to the agent.
  • core-paths (M, deprecated): openai/error|pagination|resource|streaming|uploads → openai/core/*.
  • request-options (M): list({ headers }) → list({}, { headers }). It is detected syntactically because 7.x types those keys ?: never, so the compiler message doesn't name them.
  • http-agent (A): the agent picks one of undici ProxyAgent, undici Agent or a custom fetch, and the choice goes to Decisions for you.
  • file-from-path (A): the agent picks fs.createReadStream or toFile(…, name), and the choice goes to Decisions for you.
  • run-functions, page-helpers, page-classes, removed-exports, tool-call-union (5.12.1), function-output-union (6.0): all assisted (A).

Behavior notes:

  • error-headers, web-response, encoded-path-params: reported as findings. Each has a syntactic detect for what the compiler can't see through any/unknown:
    • catch (e) { e.headers['retry-after'] }
    • res.body.pipe(...) after asResponse()
    • encodeURIComponent(id) passed to an SDK method
  • platform-minimums, zod-helpers, builtin-fetch, global-types, client-subclass: reported as decisions, each shown only with evidence from the repository (applies()). The evidence covers Node/TypeScript pins, zod below the ^3.25 || ^4 peer, openai/helpers/zod importers, proxy agents and global dispatchers, tsconfig lib without DOM, and extends OpenAI.
  • also-review: a decision pointing to the guide.

Pack settings:

  • requiresBuild: true: 7.x exports explicit subpaths only, where 4.x exported ./*. A deep import that moduleResolution: node resolves from disk can therefore fail in Node or a bundler.
  • No release group: openai-node publishes only openai, and @openai/agents is on its own 0.x line.

Ground-truth repositories

All are public, pinned at the parent of a merged upgrade, and installed with scripts off. They were drafted with pnpm packs:truth (with --also for what the upgrade moved because of openai) and assigned by hand before scoring, then left unchanged.

Repository Parent Upgrade Versions Expected
zeikar/charivo b9a476d9 ef509fde+67ae240f 4.104.0 → 7.10.0 3 tool-call-union
openai/openai-guardrails-js 6c13ba08 #122 4.104.0 → 7.9.0 (+ @openai/agents 0.17.2, zod 4.5.4) 11 generic
wardby/wardby d46ec64d #231 4.104.0 → 7.30.0 2 generic
csnyder256/ux-struggle-detector b7308489 #11 4.104.0 → 7.23.0 2 generic
grantsingleton/batch-ai 05c7e9f1 #14 4.83.0 → 7.4.0 (+ zod 4.4.3) 2 generic
digitalchokro/askchokro 047fc448 #31 4.104.0 → 7.4.0 0
microsoft/prompty (runtime/typescript) e878b6b7 #527 4.104.0 → 7.10.0 0

I found these by web search and by mining the npm registry for packages whose published manifests moved openai ^4 → ^7. The screened-out candidates and the reason for each are listed in RESEARCH.md.

Results

Precision Recall TP FP FN
Breaking 42.86% 15% 3 4 17
Overall 5% 15% 3 56 17
tool-call-union 100% 33% 1 0 2

The pack's own rules and detectors add no false positive in any of the seven repositories. Every false positive comes from check's generic findings:

  • 4 breaking: guardrails examples/…multiturn_with_prompt_injection_detection.ts:331, 413, 428, 430. These are real TS2345/TS2769 errors at 7.9.0. But examples/ is outside the repository's only tsconfig, so the upgrade didn't touch it and the tsconfig-scoped truth doesn't list it. They stay false positives.
  • 52 non-breaking: prompty. Its workspace packages import an unbuilt @prompty/core, so check skips the compile and lists type-diff sites as unverified.

Misses:

  • charivo :119: found, but as generic. The compiler message (Property 'name' does not exist on type '{}') names nothing of openai, so the rule does not claim it.
  • charivo :135: a knock-on parameter change with no diagnostic.
  • guardrails client.ts ×10: a client-subclass rework for withOptions() cloning with no compiler error. The client-subclass decision names this file, but decisions aren't scored.
  • wardby ×2, batch-ai ×2: optional type cleanups.
  • ux-struggle-detector ×2: a request-parameter change (max_tokens → max_completion_tokens), which the pack never makes by design.

What the engine could not express (why this is a candidate)

  1. Companion planning. check/companions.ts moves packages that depend or peer on the lead, but never the lead's own peers or a dependent the upgrade moved. batch-ai keeps zod 3.24.2 even though openai 7 peers on zod ^3.25 || ^4.0. guardrails' @openai/agents and zod are left behind too. Both repos therefore fail with.
  2. Compile scope. check compiles importers outside every tsconfig, while packs:truth compiles tsconfigs only. That mismatch is the source of all 4 breaking false positives above.
  3. applies() only sees the declared inventory, so the pack declares source globs to find helper imports and subclasses.
  4. With --no-llm, an assisted site that wasn't rewritten is labelled in the PR body by its symbol path rather than its rule. The agent's task does use the rule.

With 1 and 2 fixed and the same truth, the breaking predictions left would be the three true positives; pack test --write then needs a rerun.

CI: the Packs workflow runs pack test and will fail on this PR for the reasons above. It is a draft until the engine changes land.

uptide pack test output

Full output of pnpm uptide pack test openai --offline --write (summary inline)
checking openai 4.104.0 → 7.10.0 in zeikar/charivo@b9a476d9acb5
checking openai 4.104.0 → 7.9.0 in openai/openai-guardrails-js@6c13ba08ab02
checking openai 4.104.0 → 7.30.0 in wardby/wardby@d46ec64dc0be
checking openai 4.104.0 → 7.23.0 in csnyder256/ux-struggle-detector@b73084896208
checking openai 4.83.0 → 7.4.0 in grantsingleton/batch-ai@05c7e9f132b3
checking openai 4.104.0 → 7.4.0 in digitalchokro/askchokro@047fc4485538
checking openai 4.104.0 → 7.10.0 in microsoft/prompty@e878b6b7789f
uptide pack test openai · candidate
fixtures      6 cases, 37 sites · pass
ground truth
  zeikar/charivo@b9a476d9acb5                  openai 4.104.0 → 7.10.0  predicted 2, expected 3, false positives 0, false negatives 1
  openai/openai-guardrails-js@6c13ba08ab02     openai 4.104.0 → 7.9.0  predicted 5, expected 11, false positives 4, false negatives 10
    ✗ @openai/agents: the upgrade moved it to 0.17.2, check leaves it
    ✗ zod: the upgrade moved it to 4.5.4, check leaves it
  wardby/wardby@d46ec64dc0be                   openai 4.104.0 → 7.30.0  predicted 0, expected 2, false positives 0, false negatives 2
  csnyder256/ux-struggle-detector@b73084896208 openai 4.104.0 → 7.23.0  predicted 0, expected 2, false positives 0, false negatives 2
  grantsingleton/batch-ai@05c7e9f132b3         openai 4.83.0 → 7.4.0  predicted 0, expected 2, false positives 0, false negatives 2
    ✗ zod: the upgrade moved it to 4.4.3, check leaves it
  digitalchokro/askchokro@047fc4485538         openai 4.104.0 → 7.4.0  predicted 0, expected 0, false positives 0, false negatives 0
  microsoft/prompty@e878b6b7789f               openai 4.104.0 → 7.10.0  predicted 52, expected 0, false positives 52, false negatives 0

  rule                         precision  recall    tp   fp   fn
  generic                             2%      6%     1   57   16
  tool-call-union                   100%     33%     1    0    2
  overall (sites)                     5%     15%     3   56   17
  breaking (sites)                   43%     15%     3    4   17

false positives (56)
  openai/openai-guardrails-js@6c13ba08ab02  examples/basic/multiturn_with_prompt_injection_detection.ts:331  generic (breaking)
  openai/openai-guardrails-js@6c13ba08ab02  examples/basic/multiturn_with_prompt_injection_detection.ts:413  generic (breaking)
  openai/openai-guardrails-js@6c13ba08ab02  examples/basic/multiturn_with_prompt_injection_detection.ts:428  generic (breaking)
  openai/openai-guardrails-js@6c13ba08ab02  examples/basic/multiturn_with_prompt_injection_detection.ts:430  generic (breaking)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:58  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:71  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:73  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:91  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:92  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:103  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:104  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:116  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:117  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:134  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:138  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:142  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-executor.ts:143  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-models.ts:75  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-models.ts:83  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-models.ts:85  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-models.ts:201  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-models.ts:212  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/azure-models.ts:213  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:95  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:108  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:110  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:128  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:129  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:140  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:141  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:153  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:154  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:171  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:176  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:193  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/src/executor.ts:207  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/tests/integration.test.ts:95  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/foundry/tests/integration.test.ts:100  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:64  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:77  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:79  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:99  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:100  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:111  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:112  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:124  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:125  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:218  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/executor.ts:222  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/models.ts:71  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/models.ts:100  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/src/models.ts:102  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/tests/integration.test.ts:125  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/tests/integration.test.ts:126  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/tests/integration.test.ts:340  generic (unverified)
  microsoft/prompty@e878b6b7789f  packages/openai/tests/integration.test.ts:341  generic (unverified)

false negatives (17)
  zeikar/charivo@b9a476d9acb5  packages/llm/src/openai-tool-format.ts:135  tool-call-union
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:9  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:31  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:44  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:51  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:81  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:121  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:134  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:141  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:171  generic
  openai/openai-guardrails-js@6c13ba08ab02  src/client.ts:214  generic
  wardby/wardby@d46ec64dc0be  src/providers/llm/openai.ts:107  generic
  wardby/wardby@d46ec64dc0be  src/providers/llm/openai.ts:188  generic
  csnyder256/ux-struggle-detector@b73084896208  src/lib/providers/openai.ts:24  generic
  csnyder256/ux-struggle-detector@b73084896208  src/lib/providers/openai.ts:66  generic
  grantsingleton/batch-ai@05c7e9f132b3  src/index.ts:32  generic
  grantsingleton/batch-ai@05c7e9f132b3  src/index.ts:35  generic

reported under another rule (1)
  zeikar/charivo@b9a476d9acb5  packages/llm/src/openai-tool-format.ts:119  generic, expected tool-call-union

FAIL openai: 4 false positives among breaking findings

Smoke test (not ground truth)

I ran uptide fix openai --no-llm --keep-temp locally on uptide-test/alanagoyal, upgrading openai 4.104.0 → 7.33.0.

  • The pack was forced active. Only verified packs are active, so I edited this pack's verification.json locally for the run. The edit was never committed and was restored right after.
  • No LLM key was available, so all 5 compiler errors stay manual. They are the 5.12.1 tool-call union (tc.function.*), which tool-call-union claims.
  • Decisions: Node 24.x and TypeScript ^5 already meet the floor, so only the general review line appears.
  • Nothing was pushed or published. The stored PR body:
PR body produced by uptide fix openai
## Upgrade openai 4.104.0 → 7.33.0

**Review required before merging.** The code was migrated to openai 7. New type errors remain.

Assisted fixes disabled (--no-llm).

| | |
|---|---|
| **Verified by** | ✅ the openai migration pack · ❌ type-check · ❌ build · ✅ tests (1 run) |
| **Risk** | High: manual sites left; unverified sites |
| **Changes** | 5 sites · 2 changed files · 0 auto-fixed · 0 fixed by the agent (LLM) · 5 manual |
| **Types** | ❌ the upgrade broke 5 · Uptide left 5 |
| **Behavior** | ⚠️ not checked |
| **Tests** | ✅ 1 workspace passed · the `test` script of the repository |

<details><summary>Moved with the upgraded packages · 37 transitive changes</summary>

Inside the upgraded packages’ own dependency subtrees; every other resolution is unchanged.

- `openai` 4.104.0 → 7.33.0 removed @types/node 18.19.130; removed @types/node 20.11.24; removed @types/node-fetch 2.6.13; removed abort-controller 3.0.0; removed agentkeepalive 4.6.0; removed asynckit 0.4.0; removed call-bind-apply-helpers 1.0.2; removed combined-stream 1.0.8; removed delayed-stream 1.0.0; removed dunder-proto 1.0.1; removed es-define-property 1.0.1; removed es-errors 1.3.0; removed es-object-atoms 1.1.1; removed es-set-tostringtag 2.1.0; removed event-target-shim 5.0.1; removed form-data 4.0.6; removed form-data-encoder 1.7.2; removed formdata-node 4.4.1; removed function-bind 1.1.2; removed get-intrinsic 1.3.0; removed get-proto 1.0.1; removed gopd 1.2.0; removed has-symbols 1.1.0; removed has-tostringtag 1.0.2; removed hasown 2.0.4; removed humanize-ms 1.2.1; removed math-intrinsics 1.1.0; removed mime-db 1.52.0; removed mime-types 2.1.35; removed ms 2.1.3; removed node-domexception 1.0.0; removed node-fetch 2.7.0; removed tr46 0.0.3; removed undici-types 5.26.5; removed web-streams-polyfill 4.0.0-beta.3; removed webidl-conversions 3.0.1; removed whatwg-url 5.0.0.

</details>

### What changed

**1. OpenAI.ChatCompletionMessageToolCall#function** · 5 sites · manual
Manual migration required: no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual.
<details><summary>Diff and reasoning</summary>

Representative diff was not retained in this stored run.

- `app/api/chat/route.ts` (3)
- `evals/chat.eval.ts` (2)

no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual

no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual

no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual

no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual

no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual

</details>

### Worth a look

- Manual: `route.ts:321` — no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual.
- Manual: `route.ts:318` — no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual.
- Manual: `route.ts:317` — no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual.
- Manual: `chat.eval.ts:194` — no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual.
- Manual: `chat.eval.ts:192` — no mechanical rule for this reported site; assisted fixes disabled (--no-llm), left manual.
- Resolve 5 new type errors before merging (see verification details).

### Decisions for you

- Also review the [openai migration guide](https://github.com/openai/openai-node/blob/v7.32.0/MIGRATION.md) for the 5.0 changes the compiler cannot see in this repository (pagination internals, CommonJS resource classes), and the 6.0 and 7.0 changelog entries.

<details><summary>Verification details</summary>

Type errors: baseline 0; target 5; after 5; 5 new. Verification: FAIL.

Build after the bump in `.`: `next build` — failed

| Workspace | Baseline tests | Target tests |
|---|---|---|
| . | passed | passed |

- `.`: the `test` script of the repository — `node --import tsx --test tests/*.test.ts`

**Remaining errors**
- app/api/chat/route.ts:317 TS2339: Property 'function' does not exist on type 'ChatCompletionMessageToolCall'.
  Property 'function' does not exist on type 'ChatCompletionMessageCustomToolCall'.
- app/api/chat/route.ts:318 TS2339: Property 'function' does not exist on type 'ChatCompletionMessageToolCall'.
  Property 'function' does not exist on type 'ChatCompletionMessageCustomToolCall'.
- app/api/chat/route.ts:321 TS2339: Property 'function' does not exist on type 'ChatCompletionMessageToolCall'.
  Property 'function' does not exist on type 'ChatCompletionMessageCustomToolCall'.
- evals/chat.eval.ts:192 TS2339: Property 'function' does not exist on type 'ChatCompletionMessageToolCall'.
  Property 'function' does not exist on type 'ChatCompletionMessageCustomToolCall'.
- evals/chat.eval.ts:194 TS2339: Property 'function' does not exist on type 'ChatCompletionMessageToolCall'.
  Property 'function' does not exist on type 'ChatCompletionMessageCustomToolCall'.

**Tests: .**
```text
TAP version 13
# Subtest: declares the complete mobile support matrix explicitly
ok 1 - declares the complete mobile support matrix explicitly
  --- (repeated 176 times)
  duration_ms: 3.231196
  type: 'test' (repeated 175 times)
  ... (repeated 176 times)
# Subtest: sends every unsupported mobile app to Notes
ok 2 - sends every unsupported mobile app to Notes
  duration_ms: 0.964052
… (test output truncated here; all tests passed)
```

**Run notes**

- `APIError.headers` is a Web `Headers` in openai 5: `error.headers[name]` is undefined; read `error.headers.get(name)`
- `asResponse()`/`withResponse()` return a Web `Response` in openai 5: node-fetch members (`body.pipe`, `headers.raw()`, `buffer()`) are gone
- openai 5 URI-encodes path parameters itself: an id passed through `encodeURIComponent` is encoded twice
- openai-node migration guide, v7.32.0 (read 2026-10-14): https://github.com/openai/openai-node/blob/v7.32.0/MIGRATION.md
- openai-node migration guide as released with 5.0.0: https://github.com/openai/openai-node/blob/v5.0.0/MIGRATION.md
- openai-node changelog, 5.0.0 to 7.32.0 (read 2026-10-14): https://github.com/openai/openai-node/blob/v7.32.0/CHANGELOG.md
- openai 5.12.1: tool calls become a function | custom union: https://github.com/openai/openai-node/releases/tag/v5.12.1
- openai 6.0.0: function call outputs widen: https://github.com/openai/openai-node/releases/tag/v6.0.0
- openai 7.0.0: Node.js 22 required: https://github.com/openai/openai-node/releases/tag/v7.0.0

</details>

<details><summary>Run details</summary>

Uptide 0.12.1 · agent disabled (--no-llm) · 0.0k/0.0k tokens · $0.00 · 4 min

Uptide commit: `40e870b6daebbc2301b17536434fca2b9d5c8af2` (working-tree changes)
Branch: `uptide/openai-7.33.0`
Tier: verified (a migration pack covers this upgrade)
Verified at: 2026-10-14T07:10:06.426Z
Verified commit: `10c85e2210b4bfc6413596223c2068799c694e17`
Verified tree: `68de481fd3864a230da21afaf1bcdf75c9da4617`

</details>

(The test log inside is shortened here; "Tier: verified" and "Verified by ✅ the openai migration pack" reflect the local forced activation described above.)

Checklist

  • Every rule and note comes from a source listed in meta.sources
  • Every rule that rewrites or detects has a fixture case, with a site it must leave alone (keep)
  • Ground truth: public repositories at the commit before their upgrade; expected findings read from their own upgrade commit and the compiler, never copied from Uptide's output
  • At least two public repositories for verified; otherwise the pack ships as a candidate (7 public repositories; candidate because of the 4 engine false positives and 2 left-behind upgrades)
  • pnpm uptide pack test <package> passes and its output is pasted above; verification.json written with --write: it fails (4 breaking false positives, all from check's generic compile). The output is pasted above, and verification.json records candidate via --write.
  • pnpm lint && pnpm typecheck && pnpm test pass (core 1548 passed / 32 skipped, CLI 438 passed; pnpm lint reports one pre-existing warning in next/platform.test.ts)
  • Changeset added (patch: a candidate pack, the dependency stays generic)
  • No private code, names or paths anywhere: fixtures are synthetic, ground truth is public

🤖 Generated with Claude Code

https://claude.ai/code/session_01FX2nBoNEvDh9v71yDhEzxL


Generated by Claude Code

lucasbraum added a commit that referenced this pull request Oct 14, 2026
Candidate-pack smoke tests previously required editing verification.json
to pretend the pack was verified. This adds a local-only opt-in without
changing any pack record or ground truth.

- Add `--allow-candidate` to `check` and `fix`, including worker
requests, companion preflight and alternate leads. Only registered packs
with an explicit candidate record qualify; missing, blocked and
inconsistent records keep the existing behavior, and verified packs
remain unchanged.
- Reject the flag in `scripts/action.ts`, the Action runner, the Action
subprocess boundary and CLI invocations in GitHub Actions. Reject it
with `--pr`, `--yes` or publication intent before migration work.
Persisted candidate runs also refuse later publication.
- Show **candidate pack, not verified** in terminal output, stored
report.json and the generated PR body preview. Internal reports add
optional `packWarning`; the `uptide.run/v1` projection uses its existing
`error` field, with no schema or enum changes.
- Use the claiming pack rule id for assisted sites when no transform
applies, including `--no-llm`. The regression recreates OpenAI tool-call
union sites from the PR #130 smoke case synthetically; no third-party
repository or pack truth was modified.
- Document the smoke workflow in CONTRIBUTING “Write a pack”, README and
command/pack docs; include a patch changeset.

Validation: regression tests failed first (candidate eligibility, local
execution/banner, Action/publication refusals and tool-call rule
labels). `pnpm lint` and `pnpm typecheck` pass. `pnpm test --
--maxWorkers=4` passes: 2,097 tests passed, 31 network-gated tests
skipped. The first shell run hit localhost permission errors; a host run
had one existing compiler-test timeout under concurrent load. The final
full run used four workers with unchanged timeouts.

Created in an isolated git worktree, installed with `--frozen-lockfile
--ignore-scripts`, signed off with the commit author's identity, and
rebased on current main before opening this PR.

Fixes
https://linear.app/uptide/issue/UPT-51/fix-allow-candidate-for-local-smoke-tests-of-unverified-packs

Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
lucasbraum added a commit that referenced this pull request Oct 15, 2026
… a blocker (UPT-50) (#142)

## What and why


[UPT-50](https://linear.app/uptide/issue/UPT-50/plan-peers-and-dependencies-the-target-version-requires),
found by the openai pack (#130). The planner moved a
package's release group (#135, `companions.ts`) but never a package the
**target version requires**. openai 7 peers on `zod ^3.25 || ^4.0`, yet
batch-ai stayed on zod 3.24.2.

This builds on #135; it does not change it. A new step,
`check/required.ts`, runs after the release group:

- **What a planned version declares.** Take every planned version: the
lead's target and each companion it moves. When one declares a peer or
dependency range the installed copy rejects, that package moves to the
**lowest release of its line** that satisfies the range. A line is one
major, or one minor for 0.x (what a caret range reaches). Example: zod
3.24.2 → 3.25.0.
- The rule applies only when the upgrade **introduces** the violation,
i.e. the installed host accepted the installed copy. A range the
repository already breaks today is not this upgrade's (see morphic
below).
- **What depends on the lead.** A direct dependency whose own dependency
range rejects the lead's target moves the same way, within its line.
- **A required move across a major is never made.**
- A peer or a dependent becomes a **blocker** with the exact range:
`openai 7.9.0 needs zod ^4.0.0; installed 3.25.76, and only a new major
satisfies it (zod 4.0.0)`.
  - A plain dependency gets its own nested copy. That is not a blocker.
- Blockers appear in `check` (`✗ blocked: … a new major, never made by
Uptide`), in the PR body, and in `pack test` (`✗ blocked: …`, and on
each `with` entry they keep behind).
- `fix` still runs, leaves the blocked package where it is, and rates
the risk High ("a required move is blocked").
- **Shown apart from release-group companions.** A required move is a
companion marked `required`, so `fix` installs it in the same single
install. The PR body lists it separately, next to #135's "Left where
they are":
  ```
Upgraded with openai, so the install stays consistent: ← release group,
unchanged
  Moved because the new version requires it:
- `zod` 3.24.2 → 3.25.0 (moved because openai 7.4.0 requires zod ^3.25
|| ^4.0)
Blocked: a required move only a new major makes, which Uptide never
makes:
- @openai/agents 0.1.11 depends on openai ^5.20.2; only a new major
accepts openai 7.9.0 (@openai/agents 0.15.0, which needs zod ^4.0.0;
installed 3.25.76)
Left where they are (not released with it): ← #135, unchanged
  ```
- **The install scope guard allows exactly these moves.** The allowed
set is still the lead plus its companions, so a planned move is allowed.
Testing the other half turned up a gap on npm: a root dependency the
user declares (`zod`) could change without the plan saying so, because
npm resolves it as a moved package's peer and so it counted as inside
openai's subtree. pnpm and Yarn already refused that. Now on npm too, an
importer's declared dependency changes only when the plan names it
(`lock-guard.ts`).

Not in this PR: UPT-54 (@types stub removal) touches the same planner
and comes after this.

## How it was verified

**Failing tests first** (the commit `test(check): …`): 14 tests fail on
main's code and pass here, plus one more added with the refinement.
- `check/required.test.ts`, synthetic, modeled on #130's repositories:
  - batch-ai: zod 3.24.2 → 3.25.0; a prerelease is skipped.
- openai-guardrails-js: `@openai/agents` 0.1.11 only reaches openai 7 at
0.15.0, a new 0.x line that needs zod 4. This is a blocker naming both,
and neither package is moved.
- A within-line dependent that moves, plus what its new release then
requires.
- A peer that needs a new major (blocker); a 0.x minor treated as a
major.
- A plain dependency: moves within its major, or keeps a nested copy (no
blocker).
  - Workspace visibility (sibling vs. hoisted root).
  - Release-group companions unchanged next to a required move.
  - A requirement the install already breaks (morphic).
- `fix/managers/required-scope.test.ts`, for npm, pnpm and Yarn: the
lead plus the required move is allowed; the required package moving
unplanned is refused (failed on npm before); anything else moving next
to the plan is refused.
- `fix/generic.test.ts`:
- `fix` installs the required move in one install (`also`), and the PR
body names it.
- With a blocker, `fix` moves nothing extra, and the PR body names the
blocker.
- PR body and risk (`fix/report.test.ts`), `check` output
(`format-check.test.ts`), and `pack test` accounting
(`pack-test.test.ts`: a required move counts as moved, a blocker is
attached to what it keeps behind).

**Re-scored every registered pack, before (main `9ba005a`) and after
(this branch).** Same truth, scripts off:

| Pack | Before: breaking P / R (tp fp fn) | After: breaking P / R (tp
fp fn) | Overall P / R before → after | Gate before → after |
| --- | --- | --- | --- | --- |
| ai | 100% / 82% (9 0 2) | 100% / 82% (9 0 2) | 100% / 89% → same |
PASS → PASS |
| lucide-react | 100% / 100% (11 0 0) | same | 100% / 100% → same | PASS
→ PASS |
| next | 100% / 85% (11 0 2) | same | 100% / 80% → same | PASS → PASS |
| react | 100% / 80% (57 0 14) | same | 100% / 81% → same | FAIL → FAIL
¹ |
| @sentry/browser | 100% / 100% (3 0 0) | same | 100% / 100% → same |
PASS → PASS |
| @sentry/nextjs | 100% / 100% (8 0 0) | same | 100% / 100% → same |
PASS → PASS |
| @sentry/react | 100% / 100% (2 0 0) | same | 100% / 100% → same | PASS
→ PASS |
| stripe | 100% / 90% (18 0 2) | same | 100% / 90% → same | PASS → PASS
|
| vitest | 100% / 50% (16 0 16) | same | 0% / 50% → same | PASS → PASS |
| zod | 100% / 74% (59 0 21) | same | 100% / 85% → same | PASS → PASS |

**Breaking precision does not drop for any pack.**
- The only plan change: in `ai` (vercel/chatbot), `swr` 2.3.3 → 2.4.1
moves because `@ai-sdk/react` 4.0.10 requires `swr ^2.4.1`. Its scores
are unchanged.
- The first after-run did drop `ai` precision: in miurla/morphic, react
19.2.0 → 19.2.1 moved and produced 59 false positives, 4 of them
breaking. The installed `@ai-sdk/react` 3.0.182 already declares `react
~19.1.2 || ^19.2.1`, so that violation is the repository's today, not
the upgrade's. The `fix(check): plan only requirements the upgrade
introduces` commit fixes it, with a regression test. Then `ai` was
re-scored.
- The other packs were scored on the commit before that fix, which only
narrows the rule. None of them had any required move or blocker in that
run.

¹ `react` fails the same way on both sides for an environmental reason:
tldraw/tldraw pins `yarn@4.7.0` and this container only has Yarn 1.22.
The other `react` repositories score the same.

**`pack test openai` on #130's truth**, from throwaway worktrees (main +
#130 and this branch + #130, merged locally, never pushed; #130 and its
truth are untouched):

```
  openai/openai-guardrails-js@6c13ba08ab02     openai 4.104.0 → 7.9.0  predicted 5, expected 11, false positives 4, false negatives 10
    ✗ blocked: @openai/agents 0.1.11 depends on openai ^5.20.2; only a new major accepts openai 7.9.0 (@openai/agents 0.15.0, which needs zod ^4.0.0; installed 3.25.76)
    ✗ @openai/agents: the upgrade moved it to 0.17.2, check leaves it (blocked: …)
    ✗ zod: the upgrade moved it to 4.5.4, check leaves it (blocked: …)
  grantsingleton/batch-ai@05c7e9f132b3         openai 4.83.0 → 7.4.0  predicted 0, expected 2, false positives 0, false negatives 2
    with zod 3.24.2 → 3.25.0 (moved because openai 7.4.0 requires zod ^3.25 || ^4.0)
  microsoft/prompty@e878b6b7789f               openai 4.104.0 → 7.10.0  predicted 52, expected 0, false positives 52, false negatives 0
    ✗ blocked: @azure/ai-projects 2.4.0 depends on openai ^6.16.0; no release accepts openai 7.10.0
  breaking (sites)                   43%     15%     3    4   17      (before: 43% 15% 3 4 17)
FAIL openai: 4 false positives among breaking findings
```

- **batch-ai's zod move is no longer missed.** That is UPT-50's done
condition. Its upgrade chose 4.4.3; check picks the lowest release of
the same major that satisfies the range, and `with` compares names only.
- **guardrails is the needs-a-major case, and it reports the blocker
with the exact ranges.** Its real upgrade moved both packages across
their majors (agents 0.1 → 0.17, zod 3 → 4). Uptide never makes such a
move, so the pack's `with` stays unmet there, now with the reason.
- **prompty gets a new, accurate blocker:** no `@azure/ai-projects`
release accepts openai 7 yet.
- Scores are unchanged. #130 still fails its gate on its 4 compile-scope
false positives, its engine gap 2, which is outside this PR.

**Repository checks:**
- `pnpm lint`: only the warning and info that were already there.
- `pnpm typecheck` passes.
- `pnpm test`: core 1,667 passed / 32 skipped, CLI 453 passed.

## Checklist

- [x] Tests for the change (a bug fix starts with a failing test)
- [x] `pnpm lint && pnpm typecheck && pnpm test` pass
- [x] README and docs updated if behavior changed
(`docs/architecture.md` Companions, `docs/packs.md` `with`, README one
line)
- [x] Changeset added if a user would notice
(`.changeset/required-moves.md`, minor)
- [x] No new dependency, or it was agreed in an issue
- [x] No private code, names or paths in fixtures, tests or docs
(synthetic registries modeled on public repositories)
- [x] The rules in CONTRIBUTING.md ("Rules that do not bend") still hold
(installs with scripts off; nothing moves across a major)

The task brief pointed to AGENTS.md, but there is no AGENTS.md in the
repository; I followed CLAUDE.md and CONTRIBUTING.md.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01H9K3U7AeqmsqU2z6wKKxhw

---
_Generated by [Claude
Code](https://claude.ai/code/session_01H9K3U7AeqmsqU2z6wKKxhw)_

---------

Signed-off-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
lucasbraum added a commit that referenced this pull request Oct 15, 2026
Related to [UPT-49 — Compile scope must match the repo's own
tsconfig](https://linear.app/uptide/issue/UPT-49/compile-scope-must-match-the-repos-own-tsconfig).

Explicit importer roots bypassed TypeScript project selection, so
`check` reported compiler errors in files outside the repository's own
tsconfig. In guardrails, that meant four errors in its excluded
`examples/` files.

### Changes

- Apply project scope only to compiler diagnostics. Pack rules,
patterns, inventory/config files and build-option findings remain
reportable outside a tsconfig.
- Discover include/exclude/files, inherited configs, recursive
references and workspace projects using the repository compiler.
Membership comes from parsed config roots and lazy import edges, without
building a program per config.
- Check affected files in their nearest covering default project, then a
named/referenced project when needed, and never again under every
importing consumer's options.
- Reuse the scanner's program for the workspace default config, and
release alternate programs after use.
- Without a tsconfig, keep importer-based compilation and say so in
coverage.
- Anchor missing members supplied by an unchanged dependency's
augmentation (jest-dom) at the side-effect setup import, keeping the
failing calls as evidence. This is required for this change: tsconfig
scope now type-checks test files under the app config. Without it, the
vitest pack gets **1,443 breaking false positives** on
opentelemetry-ecosystem-explorer (measured with the commit removed);
with it the pack matches main. Its module-level cache is registered with
shared-state (the `shared-state.test.ts` failure on the previous head).
- Documentation and a patch changeset.

The earlier React Router `verification.json` refresh was dropped in the
rebase: main already carries the identical record.

### Validation

Head `02f8eb4`, rebased on main `7aa3d58`.

- `pnpm lint`, `pnpm typecheck`: pass.
- Focused: `vitest run src/shared-state.test.ts src/adapters/typescript
src/check src/packs/stripe`: 357 passed.
- Full `pnpm test` on the previous rebase (onto `0b88170`): core 1763
passed, cli 456 passed.

### Resources: `check`, main vs branch

Local macOS, Node 22.20.0, `--max-old-space-size=4096`, warm offline
cache. The harness calls `check` in-process with the registered pack (as
pack test does). Two alternating rounds each (main, branch, main,
branch). Measured on engine `3895d17` against main `0881185`. The later
rebases only brought in main's own commits, so these runs were not
repeated.

| Repository / upgrade | main wall (mean) / peak RSS | branch wall
(mean) / peak RSS | Wall | RSS |
| --- | --- | --- | ---: | ---: |
| Excalidraw `9ee0b8f`, React → 19.0.0 | 38.2 s / 2.73 GiB | 40.3 s /
2.79 GiB | +5.5% | +2.2% |
| vxsuite `5cb3c7d`, Vitest → 5.0.2 | 133.8 s / 2.79 GiB | 134.5 s /
2.88 GiB | +0.5% | +3.2% |

Both are within the ~10% target. Host load averages were 4.5–7.9 on 11
CPUs.

### Pack re-score: main vs branch

Engine `3895d17` vs main `0881185`, one pack at a time with cache
eviction. Breaking TP / FP / FN:

| Pack | main | branch | Site-level result |
| --- | --- | --- | --- |
| ai | 9 / 0 / 2 | 9 / 0 / 2 | identical |
| lucide-react | 11 / 0 / 0 | 11 / 0 / 0 | identical |
| next | 11 / 0 / 2 | 11 / 0 / 2 | identical |
| react | 89 / 0 / 40 | 89 / 0 / 40 | breaking identical; +2
`unverified` sites (below) |
| react-router | 14 / 0 / 2 | 14 / 0 / 2 | identical (main then failed
only on its stale record, since fixed on main) |
| @sentry/browser | 3 / 0 / 0 | 3 / 0 / 0 | identical |
| @sentry/nextjs | 8 / 0 / 0 | 8 / 0 / 0 | identical |
| vitest | 16 / 0 / 16 | 16 / 0 / 16 | breaking identical; 3,619 fewer
`unverified` FPs on opentelemetry-ecosystem-explorer, none added |
| @sentry/react, stripe, zod | — | — | not compared locally (the
branch-side storefront fixture had no install, and one @sentry/react
repository failed to install on both sides); CI's full Pack test covers
them |

The react difference: excalidraw `dev-docs/` is part of the root
workspace. On main, the root workspace's analysis aborts with `EEXIST`
(symlinking `@types/react-dom` into `node_modules`), so main never
reports these sites. The branch analyzes it and reports
`dev-docs/src/components/Homepage/index.tsx:7` and
`dev-docs/src/theme/ReactLiveScope/index.js:10`, both `unverified`. That
`EEXIST` is an existing main bug, not changed here.

### OpenAI (#130), throwaway overlay

`uptide pack test openai` on `02f8eb4` with #130 cherry-picked, in a
throwaway worktree. Nothing was pushed.

| | #130 as recorded | this branch + #130 |
| --- | --- | --- |
| Breaking TP / FP / FN | 3 / 4 / 17 (42.86% precision) | **3 / 0 / 17
(100% precision)** |
| Overall FP | 56 | 52 |

- The four guardrails
`examples/…multiturn_with_prompt_injection_detection.ts` false positives
(`:331, :413, :428, :430`) are gone. No `examples/` site is predicted.
- `src/base-client.ts:368` is still reported (breaking).
- The remaining 52 FPs are prompty's `unverified` sites, unchanged from
#130's own run.
- The run reports #130's `verification.json` as stale because it
improves on the recorded precision. #130 should re-record after this
lands.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
One pack for openai >=4 <5 to >=7 <8, from the openai-node migration
guide and changelog pinned at v7.32.0 (read 2026-10-14): 5.0 carries
the API changes, 5.12.1 the tool-call unions, 6.0 one output widening,
7.0 the Node 22 floor. RESEARCH.md classifies every change.

Rewrites where unambiguous: named path parameters, del -> delete,
beta.chat promoted, runner event renames, removed entry points,
request-option overloads, core/* paths (deprecated). Guides for the
tool-call union, httpAgent, fileFromPath and other removals, with
options reported as decisions. Syntactic detects for APIError.headers,
node-fetch Response members and double-encoded path ids, which the
compiler cannot see through any. Decisions carry repository evidence
(Node 22, zod peer and helpers, fetch/proxies, global types, client
subclasses). requiresBuild: 7.x exports explicit subpaths only.

Ground truth: seven public 4.x -> 7.x upgrades. The pack's own rules
add no false positive, but the pack stays a candidate: check reports
four breaking sites in files outside guardrails' tsconfig, and leaves
behind zod and @openai/agents that two upgrades moved. Both are engine
gaps, documented and left for separate changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FX2nBoNEvDh9v71yDhEzxL
Signed-off-by: Claude <noreply@anthropic.com>
…quired-move fixes

Rebased on main after #138, #142 and #154 and re-ran pack test --write
on the unchanged ground truth: breaking 3 TP / 0 FP / 17 FN (100%
precision, 15% recall). It stays a candidate: check reports
openai-guardrails-js blocked on a required @openai/agents major and
prompty blocked on @azure/ai-projects' openai ^6 dependency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FX2nBoNEvDh9v71yDhEzxL
Signed-off-by: Claude <noreply@anthropic.com>
@lucasbraum
lucasbraum force-pushed the claude/keen-brahmagupta-qbatc4 branch from 526ff10 to 47b4307 Compare October 15, 2026 06:14
@lucasbraum
lucasbraum marked this pull request as ready for review October 15, 2026 06:14

Copy link
Copy Markdown
Collaborator Author

Rebased on main (0db5640, after #138, #142 and #154) and re-scored once with pnpm uptide pack test openai --write. The ground truth is unchanged.

Precision Recall TP FP FN
Breaking 100% 15% 3 0 17
Overall 5% 15% 3 52 17

It stays a candidate. The run itself measures candidate, so there is no override: check reports two repositories as blocked on companion moves.

  • openai-guardrails-js: @openai/agents 0.1.11 depends on openai ^5, and only a new major accepts openai 7 (0.15+, which needs zod 4). check reports that major as a blocker. The real upgrade made exactly that move (0.17.2 with zod 4.5.4), so its with stays unmet.
  • prompty (new since feat(check): plan what the target version requires; report a major as a blocker (UPT-50) #142): @azure/ai-projects 2.4.0 depends on openai ^6.16.0, which the planner reports as blocked. It's a regular dependency, and the real upgrade kept it, with openai 6.49.0 nested under it in the lockfile.

pack test still exits non-zero on these two blockers, so the Packs check may stay red for that reason. RESEARCH.md, verification.json, the README table and the queue reason are updated.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants