Skip to content

Refactor release hardening follow-ups#39959

Merged
steipete merged 7 commits intomainfrom
refactor/release-hardening-pass
Mar 8, 2026
Merged

Refactor release hardening follow-ups#39959
steipete merged 7 commits intomainfrom
refactor/release-hardening-pass

Conversation

@steipete
Copy link
Copy Markdown
Contributor

@steipete steipete commented Mar 8, 2026

Summary

  • fail fast when the generated host-env Swift policy is stale and sync the checked-in output
  • guard release checks against new bundled extension root dependency gaps
  • centralize provider capability quirks and table-drive the Kimi/transcript regressions
  • block PR merge when the local prep branch has unpushed commits
  • simplify models-config merge preservation so stale baseUrls are only kept when provider API still matches

Validation

  • pnpm check:host-env-policy:swift
  • pnpm release:check
  • pnpm test -- test/release-check.test.ts
  • pnpm test -- src/agents/provider-capabilities.test.ts src/agents/pi-embedded-runner-extraparams.test.ts src/agents/transcript-policy.test.ts
  • pnpm test -- src/agents/models-config.providers.openai-codex.test.ts src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts src/agents/models-config.runtime-source-snapshot.test.ts
  • pnpm build
  • pnpm check

@cursor
Copy link
Copy Markdown

cursor bot commented Mar 8, 2026

PR Summary

Medium Risk
Touches release/merge automation and agent provider request-shaping logic; regressions could block releases/merges or alter tool payload compatibility for Anthropic-style providers.

Overview
Tightens release and merge hardening by (1) regenerating the macOS HostEnvSecurityPolicy.generated.swift output and moving the Swift policy check earlier in pnpm check, (2) adding a new release-check guard that fails if bundled extension dependencies drift from the root package.json (with an explicit allowlist), and (3) blocking scripts/pr merge-verify when the local prep branch HEAD no longer matches the previously pushed/prepared SHA.

Refactors agent/provider quirks by centralizing provider capability flags (e.g., Kimi aliases) and using them to gate Anthropic tool schema/tool_choice normalization and thinking-signature preservation; related tests are converted to table-driven cases.

Adjusts models-config merge behavior so an existing baseUrl is only preserved when the provider api still matches, preventing stale URLs from surviving provider API changes (with new regression coverage).

Written by Cursor Bugbot for commit 4c71606. This will update automatically on new commits. Configure here.

@openclaw-barnacle openclaw-barnacle bot added app: macos App: macos scripts Repository scripts agents Agent runtime and tooling size: L maintainer Maintainer-authored PR labels Mar 8, 2026
@steipete steipete merged commit eba9dcc into main Mar 8, 2026
34 checks passed
@steipete steipete deleted the refactor/release-hardening-pass branch March 8, 2026 14:50
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

payloadObj.tool_choice = normalizeOpenAiStringModeAnthropicToolChoice(
payloadObj.tool_choice,
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BaseUrl-based kimi detection path becomes dead code

Medium Severity

requiresAnthropicToolPayloadCompatibility can return true via baseUrl matching (e.g., a kimi.com/coding URL with a custom provider name), but the inner guards usesOpenAiFunctionAnthropicToolSchema(model.provider) and usesOpenAiStringModeAnthropicToolChoice(model.provider) both check the provider string against the capabilities table. When the match came from baseUrl alone (provider is not "kimi-coding"), both inner checks return false, so neither tool schema nor tool_choice normalization happens. The old code applied both normalizations unconditionally once the endpoint matched. This is a regression for users who configure a kimi.com/coding endpoint with a non-standard provider key.

Additional Locations (1)

Fix in Cursor Fix in Web

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 8, 2026

Greptile Summary

This PR is a release hardening follow-up that centralizes provider-specific quirks into a new provider-capabilities.ts module, adds a bundled-extension root-dependency mirror check to the release pipeline, blocks merges when the prep branch has unpushed commits, and simplifies models-config merge logic to drop stale baseUrls only when the provider API changes.

Key changes:

  • provider-capabilities.ts — new table-driven capability registry (anthropicToolSchemaMode, anthropicToolChoiceMode, preserveAnthropicThinkingSignatures) shared by extra-params.ts and transcript-policy.ts. Kimi aliases (kimi-codekimi-coding) are handled via the existing normalizeProviderId helper.
  • extra-params.ts — the URL-based kimi endpoint detection path in requiresAnthropicToolPayloadCompatibility now has no functional effect: it enters the outer wrapper block but the inner usesOpenAiFunctionAnthropicToolSchema / usesOpenAiStringModeAnthropicToolChoice guards both receive undefined (when model.provider is absent) and resolve to the native default, so neither tools nor tool_choice get normalized. This is a regression compared to the old isKimiCodingAnthropicEndpoint code where a URL match unconditionally triggered both transformations.
  • models-config.ts — removes the hard-coded AUTHORITATIVE_IMPLICIT_BASEURL_PROVIDERS set and replaces it with shouldPreserveExistingBaseUrl which compares the api field of the existing and incoming entries. Clean, provider-agnostic approach with a new test verifying API-change-driven base URL replacement.
  • release-check.tscollectBundledExtensionRootDependencyGapErrors cross-checks bundled extension dependencies against the root package.json and fails fast when gaps deviate from the allowlist.
  • scripts/prverify_prep_branch_matches_prepared_head is inserted into merge_verify to abort when the local prep branch HEAD diverges from the prepared SHA.

Confidence Score: 3/5

  • Mostly safe, but the URL-based kimi endpoint path in extra-params.ts is now a functional no-op, which could silently break tool calls for configurations that rely on URL detection without an explicit provider: "kimi-coding" field.
  • The provider-capabilities refactor, models-config simplification, and release-check additions are clean and well-tested. The one concrete regression is in createAnthropicToolPayloadCompatibilityWrapper: when requiresAnthropicToolPayloadCompatibility fires via the kimi.com/coding URL heuristic but model.provider is not set, both inner normalization guards (usesOpenAiFunctionAnthropicToolSchema and usesOpenAiStringModeAnthropicToolChoice) receive undefined and return false, so neither the tools array nor tool_choice is transformed. Before this PR, the old isKimiCodingAnthropicEndpoint would have applied both transformations in this case. If any real deployment relies on URL-only detection, their tool payloads will be silently malformed after this change.
  • src/agents/pi-embedded-runner/extra-params.ts — specifically the interaction between requiresAnthropicToolPayloadCompatibility (URL path) and the nested usesOpenAiFunctionAnthropicToolSchema / usesOpenAiStringModeAnthropicToolChoice guards inside createAnthropicToolPayloadCompatibilityWrapper.

Last reviewed commit: 4c71606

Comment on lines +902 to +926
if (
payload &&
typeof payload === "object" &&
requiresAnthropicToolPayloadCompatibility(model)
) {
const payloadObj = payload as Record<string, unknown>;
if (Array.isArray(payloadObj.tools)) {
if (
Array.isArray(payloadObj.tools) &&
usesOpenAiFunctionAnthropicToolSchema(
typeof model.provider === "string" ? model.provider : undefined,
)
) {
payloadObj.tools = payloadObj.tools
.map((tool) => normalizeKimiCodingToolDefinition(tool))
.map((tool) => normalizeOpenAiFunctionAnthropicToolDefinition(tool))
.filter((tool): tool is Record<string, unknown> => !!tool);
}
payloadObj.tool_choice = normalizeKimiCodingToolChoice(payloadObj.tool_choice);
if (
usesOpenAiStringModeAnthropicToolChoice(
typeof model.provider === "string" ? model.provider : undefined,
)
) {
payloadObj.tool_choice = normalizeOpenAiStringModeAnthropicToolChoice(
payloadObj.tool_choice,
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL-based kimi detection is now a no-op for normalization

requiresAnthropicToolPayloadCompatibility can return true via the URL-matching path (kimi.com/coding hostname) when model.provider is not set to "kimi-coding". However, both inner normalization guards (usesOpenAiFunctionAnthropicToolSchema and usesOpenAiStringModeAnthropicToolChoice) receive undefined in that case, which resolves to the default "native" capabilities — so neither tools array nor tool_choice is ever normalized.

Before this refactor, isKimiCodingAnthropicEndpoint returning true unconditionally applied both normalizations. Now, a user who configures api: "anthropic-messages" + baseUrl: "https://api.kimi.com/coding/..." without explicitly setting provider: "kimi-coding" will silently get no transformation, breaking their tool calls.

If the URL-based fallback is intentionally kept for future extensibility, consider also driving the capability resolution from the URL match so the nested guards can fire:

// inside createAnthropicToolPayloadCompatibilityWrapper onPayload
if (payload && typeof payload === "object" && requiresAnthropicToolPayloadCompatibility(model)) {
  const payloadObj = payload as Record<string, unknown>;
  // Resolve capabilities from provider; fall back to kimi-coding caps when URL-matched
  const effectiveProvider =
    typeof model.provider === "string"
      ? model.provider
      : requiresAnthropicToolPayloadCompatibility(model) // URL match
        ? "kimi-coding"
        : undefined;

  if (Array.isArray(payloadObj.tools) && usesOpenAiFunctionAnthropicToolSchema(effectiveProvider)) {
    ...
  }
  if (usesOpenAiStringModeAnthropicToolChoice(effectiveProvider)) {
    ...
  }
}

Alternatively, if the URL-based path is no longer needed now that provider IDs are normalized, remove the hostname check from requiresAnthropicToolPayloadCompatibility to avoid dead code.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/extra-params.ts
Line: 902-926

Comment:
**URL-based kimi detection is now a no-op for normalization**

`requiresAnthropicToolPayloadCompatibility` can return `true` via the URL-matching path (kimi.com/coding hostname) when `model.provider` is *not* set to `"kimi-coding"`. However, both inner normalization guards (`usesOpenAiFunctionAnthropicToolSchema` and `usesOpenAiStringModeAnthropicToolChoice`) receive `undefined` in that case, which resolves to the default `"native"` capabilities — so neither tools array nor `tool_choice` is ever normalized.

Before this refactor, `isKimiCodingAnthropicEndpoint` returning `true` unconditionally applied both normalizations. Now, a user who configures `api: "anthropic-messages"` + `baseUrl: "https://api.kimi.com/coding/..."` *without* explicitly setting `provider: "kimi-coding"` will silently get no transformation, breaking their tool calls.

If the URL-based fallback is intentionally kept for future extensibility, consider also driving the capability resolution from the URL match so the nested guards can fire:

```ts
// inside createAnthropicToolPayloadCompatibilityWrapper onPayload
if (payload && typeof payload === "object" && requiresAnthropicToolPayloadCompatibility(model)) {
  const payloadObj = payload as Record<string, unknown>;
  // Resolve capabilities from provider; fall back to kimi-coding caps when URL-matched
  const effectiveProvider =
    typeof model.provider === "string"
      ? model.provider
      : requiresAnthropicToolPayloadCompatibility(model) // URL match
        ? "kimi-coding"
        : undefined;

  if (Array.isArray(payloadObj.tools) && usesOpenAiFunctionAnthropicToolSchema(effectiveProvider)) {
    ...
  }
  if (usesOpenAiStringModeAnthropicToolChoice(effectiveProvider)) {
    ...
  }
}
```

Alternatively, if the URL-based path is no longer needed now that provider IDs are normalized, remove the hostname check from `requiresAnthropicToolPayloadCompatibility` to avoid dead code.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c7160604d

ℹ️ 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".

Comment on lines +909 to +913
Array.isArray(payloadObj.tools) &&
usesOpenAiFunctionAnthropicToolSchema(
typeof model.provider === "string" ? model.provider : undefined,
)
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize Kimi tool payloads when baseUrl detection triggers

When requiresAnthropicToolPayloadCompatibility(model) is satisfied via the baseUrl fallback (for example, a custom provider using https://api.kimi.com/coding/), this branch still gates normalization on model.provider capabilities, so the tools[]/tool_choice rewrites are skipped. That leaves Anthropic-shaped tool payloads on a Kimi coding endpoint, which can reject tool calls; before this refactor, baseUrl detection alone applied the normalization.

Useful? React with 👍 / 👎.


const existingApi = resolveProviderApi(existing);
const nextApi = resolveProviderApi(nextEntry);
return !existingApi || !nextApi || existingApi === nextApi;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require API match before preserving existing provider baseUrl

This predicate keeps the old baseUrl whenever either side is missing api, so merge mode preserves stale endpoints from older/manual models.json entries that omit api even after the generated provider now points to a different API/backend (notably implicit openai-codex). That prevents automatic endpoint migration and can leave requests routed to incompatible URLs.

Useful? React with 👍 / 👎.

mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 8, 2026
* main: (70 commits)
  Refactor release hardening follow-ups (openclaw#39959)
  docs: clarify bot review conversation ownership (openclaw#39942)
  fix: harden talk silence timeout parsing (openclaw#39607) (thanks @danodoesdesign)
  talk: add configurable silence timeout
  transcript-policy: use named Set for anthropic signature-excluded providers
  transcript-policy: don't preserve thinking signatures for kimi-coding (openclaw#39798)
  fix: land mac universal release defaults (openclaw#33891) (thanks @cgdusek)
  Docs: clarify notarization handoff in mac release flow
  Docs: mark basic mac dist example as non-notarized
  Docs: clarify release build arch defaults for mac packaging
  macOS: default release app builds to universal binaries
  fix(issue-39839): address tool-call extra params parsing for kimi anthropic-messages
  docs: use alphabetical provider ordering
  fix: follow up openclaw#39321 and openclaw#38445 landings
  docs: note /landpr merge process
  fix: land Brave llm-context gaps (openclaw#33383) (thanks @thirumaleshp)
  feat: add Brave Search LLM Context API mode for web_search
  fix(feishu): restore @larksuiteoapi/node-sdk in root dependencies
  refactor: tighten codex inline api fallback follow-up
  macOS: set speech recognition taskHint for Talk Mode mic capture
  ...
Saitop pushed a commit to NomiciAI/openclaw that referenced this pull request Mar 8, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
GordonSH-oss pushed a commit to GordonSH-oss/openclaw that referenced this pull request Mar 9, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
jenawant pushed a commit to jenawant/openclaw that referenced this pull request Mar 10, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
sauerdaniel pushed a commit to sauerdaniel/openclaw that referenced this pull request Mar 11, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
Moshiii pushed a commit to Moshiii/openclaw that referenced this pull request Mar 11, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
Moshiii pushed a commit to Moshiii/openclaw that referenced this pull request Mar 11, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
dhoman pushed a commit to dhoman/chrono-claw that referenced this pull request Mar 11, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 14, 2026
…39959)

Partial pick — discards gutted files (models-config, pi-embedded-runner,
transcript-policy, provider-capabilities, test/release-check.test.ts).

Alive changes:
- build: fail fast on stale host-env swift policy (move check to front)
- build: guard bundled extension root dependency gaps
- fix: block merge when prep branch has unpushed commits
- HostEnvSecurityPolicy.generated.swift: restore blocked override keys

Rebrand: openclaw→remoteclaw in release-check.ts PackageJson type and
bundled extension dependency mirror validation.

Cherry-picked-from: eba9dcc
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 14, 2026
…39959)

Partial pick — discards gutted files (models-config, pi-embedded-runner,
transcript-policy, provider-capabilities, test/release-check.test.ts).

Alive changes:
- build: fail fast on stale host-env swift policy (move check to front)
- build: guard bundled extension root dependency gaps
- fix: block merge when prep branch has unpushed commits
- HostEnvSecurityPolicy.generated.swift: restore blocked override keys

Rebrand: openclaw→remoteclaw in release-check.ts PackageJson type and
bundled extension dependency mirror validation.

Cherry-picked-from: eba9dcc
alexey-pelykh added a commit to remoteclaw/remoteclaw that referenced this pull request Mar 14, 2026
…39959) (#1256)

Partial pick — discards gutted files (models-config, pi-embedded-runner,
transcript-policy, provider-capabilities, test/release-check.test.ts).

Alive changes:
- build: fail fast on stale host-env swift policy (move check to front)
- build: guard bundled extension root dependency gaps
- fix: block merge when prep branch has unpushed commits
- HostEnvSecurityPolicy.generated.swift: restore blocked override keys

Rebrand: openclaw→remoteclaw in release-check.ts PackageJson type and
bundled extension dependency mirror validation.

Cherry-picked-from: eba9dcc

Co-authored-by: Peter Steinberger <[email protected]>
senw-developers pushed a commit to senw-developers/va-openclaw that referenced this pull request Mar 17, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
V-Gutierrez pushed a commit to V-Gutierrez/openclaw-vendor that referenced this pull request Mar 17, 2026
* build: fail fast on stale host-env swift policy

* build: sync generated host env swift policy

* build: guard bundled extension root dependency gaps

* refactor: centralize provider capability quirks

* test: table-drive provider regression coverage

* fix: block merge when prep branch has unpushed commits

* refactor: simplify models config merge preservation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: macos App: macos maintainer Maintainer-authored PR scripts Repository scripts size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant