Skip to content

fix(github): wire block fields that never reached the API - #7272

Closed
waleedlatif1 wants to merge 7 commits into
stagingfrom
fix/github-block-param-wiring
Closed

fix(github): wire block fields that never reached the API#7272
waleedlatif1 wants to merge 7 commits into
stagingfrom
fix/github-block-param-wiring

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Nine GitHub block fields render, accept input, and are then discarded.

A subBlock binds to a tool param only when its id matches the param name (resolveSubBlockForParam in tools/params.ts), and the block declares no tools.config.params mapper and no canonicalParamId. So every field whose id merely resembles the param it feeds is inert: the control renders, the user fills it, and the value never reaches the request.

Found while auditing the GitHub integration. Independent of #7262 — different files, either can merge first.

What was broken

Three of these make an operation impossible rather than degraded:

Field Feeds Effect
gist_public public Every gist is created secret regardless of the Public/Secret dropdown — create_gist defaults public ?? false
reaction_content content Both reaction operations 422 on every call
milestone_title title Create Milestone unusable

Plus six inert filters/options: fork_name, fork_sort, milestone_description, milestone_state, milestone_sort, and workflow_id.

The gist one is the one I'd look at first: it silently contradicts an explicit user choice about visibility, and nothing surfaces the disagreement.

Three more, same area:

  • Update Branch Protection could not be driven at all. restrictions is required by the API and had no field; the other three required params were collapsed under mode: 'advanced' and unmarked required, so the default form showed only owner/repo/branch. The object-valued ones were also declared type: 'string' in inputs, so the handler never parsed them and sent JSON text.
  • check_star could never answer "not starred". GitHub encodes this endpoint's answer in the status line with no body — 204 starred, 404 not — and the executor rejects every non-2xx before transformResponse runs. The negative half, the one a workflow branches on, surfaced as a tool failure.
  • list_workflow_runs rendered a "Workflow ID or Filename" field for a tool with no such param, silently widening the query to every run in the repository.

Approach

Aliases, not renames. A subBlock id is persisted workflow state, so renaming one needs a _removed_ migration. canonicalParamId is also the wrong tool here — the repo reserves it for linking alternative inputs for one logical param and forbids reusing a subBlock id, and content/title are both live ids elsewhere in the block. So: an operation-keyed alias table plus a tools.config.params mapper.

Keyed by operation, not flattened. Targets collide — fork_sort and milestone_sort both feed sort — and stored block state keeps values for fields the current operation does not render, so a flat table would let a stale sibling win.

Every assignment is guarded. generic-handler merges { ...inputs, ...params(inputs) }, so an unconditional write clobbers a model-supplied value with undefined on the agent path. One test pins exactly that, and another asserts the mapper never emits undefined for any operation. No coercion in tools.config.tool.

Boolean coercion. Dropdown option ids are strings, so 'false' arrives truthy and every boolean param inverts. Coerced for the seven boolean tool params reachable from a dropdown, converting only the exact strings so an already-boolean value is untouched.

nonErrorStatuses on ToolConfig is opt-in and narrow: absent the declaration, nothing changes for any of the other 5,247 tools. Only check_star declares it.

Workflow id is percent-encoded. list_workflows reports a workflow by path (.github/workflows/ci.yml), which is the value an agent chains in, and GitHub resolves that spelling only as %2F — a real slash 404s. Verified against the live API.

Tests

Every fix is pinned by a test verified red before the fix, including a generic guard that asserts every required tool param is reachable for every operation — the check that would have caught all nine. It initially passed vacuously against vitest.setup's mocked @/tools/registry; unmocked, it reproduces exactly the four hard failures.

Note for whoever rebases

#7262 replaces raw path interpolation in these tools with the safeUrlPath* helpers. The workflow_id interpolation added here uses encodeURIComponent, which is what safeEncodedUrlPathSegment does — it should become that helper on rebase.

Verification

  • 1,549 tests pass across tools/github, blocks/, tools/index.test.ts, tools/params.test.ts
  • All 39 check:audits pass, including docs:check, tool-metadata:check, integration-catalog:check, and the subblock-ID stability check
  • Generated artifacts regenerated; semantic diff of tool-metadata.ts is exactly the one new param, no sweep-in drift
  • Type-check and biome clean

Deliberately excluded: the nullable commit.commit.author dereferences, phantom state_reason/node_id outputs, and the unreachable non-2xx branches in seven other tools. Real, but a separate PR — this one stays reviewable.

Two findings worth carrying forward

Both were defects in this PR, found in review. Recording them because each is an instance of a class, not a one-off.

Encoding is not protection — again

workflow_id: '..' silently listed every run in the repository. encodeURIComponent('..') returns '..' verbatim — a dot segment is made entirely of unreserved characters — and the WHATWG parser then removes it and pops a path segment, resolving /actions/workflows/../runs back to /actions/runs.

So the parameter added to narrow the query produced exactly the over-broad query it was meant to prevent, and did it silently. This is the third time in this batch that encoding was mistaken for protection, and it happened inside the PR fixing that class. The rule holds without exception: only value rejection neutralizes a dot segment. A dot segment inside a longer value is a different case and stays accepted — its separators survive as %2F and the parser does not decode those first.

A present-but-wrong-typed value (workflow_id: true) reached the same over-broad query through the omission branch, and is rejected for the same reason: a value the caller did supply must never be read as one they did not.

A test that could not fail

The never emits an undefined value for any alias test asserted over a map that was always empty — mapParams({ operation }) returns {} when no alias has a source, so the inner expect never executed for any operation. It passed unconditionally and gave false confidence about the exact property it named.

This is the ninth instance of the vacuous-assertion pattern across this effort, so it is a known class rather than a slip. Two shapes recurred here specifically:

  • An assertion inside a loop that never iterates. Guard the loop body count, or drive the case with real values.
  • A guard that passes against a mock. The central reachability guard initially passed vacuously against vitest.setup's mocked @/tools/registry — every tool resolved to undefined and was skipped. It needed vi.unmock('@/tools/registry') before it reproduced the four real failures.

The check that catches both: revert the fix and confirm the test goes red. Every fix in this PR was verified that way.

Path-safety note for the rebase

Only one identifier in this diff is newly trimmed — workflow_id — and list_workflow_runs is a GET, so it belongs on the trimming helper (safeEncodedUrlPathSegment), not on strictUrlPathSegment, which #7262 added to refuse padded identifiers on state-changing requests. No mutating request in this diff interpolates a newly-trimmed identifier: check_star is a GET and its URL is untouched, and update_branch_protection's URL is untouched here (its .trim() is a blankness test on a body field that returns the original value unmodified).

Nine GitHub block fields render, accept input, and are then discarded. A
subBlock binds to a tool param only when its id matches the param name
(`resolveSubBlockForParam`), and the block declares no `tools.config.params`
mapper and no `canonicalParamId`, so every field whose id merely resembles the
param it feeds is inert.

Three of them make an operation impossible rather than degraded:

- Create Gist's Public/Secret dropdown feeds `gist_public`, never `public`, and
  `create_gist` defaults `public ?? false` — so every gist is created secret no
  matter what the user picks. This one silently contradicts an explicit choice
  about visibility.
- Both reaction operations require `content`, but the field is
  `reaction_content`, so every call 422s.
- Create Milestone requires `title`, but the field is `milestone_title`.

Adds an operation-keyed alias table plus a mapper. Keyed by operation because
targets collide — `fork_sort` and `milestone_sort` both feed `sort` — and stored
block state keeps values for fields the current operation does not render, so a
flat table would let a stale sibling win. Every assignment is guarded: the
handler merges `{ ...inputs, ...params(inputs) }`, so an unconditional write
would clobber a model-supplied value with `undefined` on the agent path.

The ids are aliased rather than renamed because a subBlock id is persisted
workflow state; renaming needs a `_removed_` migration.

Also in the same mechanism: dropdown option ids are strings, so `'false'`
arrives truthy and every boolean param inverts. Coerced for the seven boolean
tool params reachable from a dropdown.

Update Branch Protection could not be driven at all: `restrictions` is required
by the API and had no field, and the other three required params were collapsed
under advanced mode and unmarked required, so the default form showed only
owner/repo/branch. Adds the field, marks all four required, and declares the
object-valued ones `json` in `inputs` so the handler parses them instead of
sending JSON text.

check_star could never answer "not starred". GitHub encodes this endpoint's
answer in the status line with no body — 204 starred, 404 not — and the executor
rejects every non-2xx before `transformResponse` runs, so the negative half was
unreachable and surfaced as a tool failure. Adds an opt-in `nonErrorStatuses` to
ToolConfig; absent that declaration nothing changes for any other tool.

list_workflow_runs rendered a "Workflow ID or Filename" field for a tool with no
such param, silently widening the query to every run in the repository. Adds the
param and the workflow-scoped endpoint. The id is percent-encoded because
`list_workflows` reports a workflow by `path` (`.github/workflows/ci.yml`) and
GitHub resolves that spelling only as `%2F` — a real slash 404s.

Every fix is pinned by a test verified red first, including a generic guard that
asserts every required tool param is reachable for every operation — the check
that would have caught all of these.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 29, 2026 6:11am

Request Review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@greptile-apps

greptile-apps Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR repairs GitHub block-to-tool parameter wiring and several related request/response contracts.

  • Maps persisted, operation-specific field aliases to canonical tool parameters without clobbering direct model inputs.
  • Normalizes dropdown booleans and the branch-protection replacement payload.
  • Supports workflow-scoped run listing and GitHub’s status-based negative response for star checks.
  • Updates tests, generated metadata, integration documentation, and deployment catalog data.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/blocks/blocks/github.ts Adds operation-scoped parameter aliases, boolean normalization, branch-protection fields, and V2 mapper inheritance.
apps/sim/tools/github/update_branch_protection.ts Models nullable replacement settings and always emits GitHub’s four required protection keys.
apps/sim/tools/github/check_star.ts Treats GitHub’s documented 404 response as the negative star-check result.
apps/sim/tools/github/list_workflow_runs.ts Adds optional workflow scoping with encoded workflow identifiers and dot-segment rejection.
apps/sim/tools/index.ts Allows explicitly declared non-error HTTP statuses to reach a tool’s response transformer.
apps/sim/tools/types.ts Adds the opt-in nonErrorStatuses contract to HTTP tool configuration.

Reviews (9): Last reviewed commit: "fix(github): let ListWorkflowRunsParams ..." | Re-trigger Greptile

Comment thread apps/sim/blocks/blocks/github.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 12 files

Confidence score: 2/5

  • In apps/sim/blocks/blocks/github.ts, explicit null branch-protection settings are rejected as missing, so documented disable requests never reach GitHub; update required-parameter validation to allow null for these JSON settings.
  • In apps/sim/tools/github/list_workflow_runs.ts, workflow_id values of . or .. can be normalized into a request for all repository runs, creating an unintended data-scope expansion; reject dot-segment values, including surrounding whitespace.
  • In apps/sim/blocks/blocks/github.ts, selecting All for github_list_branches sends protected=all instead of a valid boolean, so the GitHub filter request is invalid; map All to undefined and omit the query parameter.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/tools/github/list_workflow_runs.ts">

<violation number="1" location="apps/sim/tools/github/list_workflow_runs.ts:95">
P2: When `workflow_id` is `.` or `..` (including surrounding whitespace), `encodeURIComponent` leaves the dot segment unchanged and `new URL` normalizes it, so `..` silently requests all repository runs. Reject dot-segment identifiers before building the scoped path.</violation>
</file>

<file name="apps/sim/blocks/blocks/github.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.ts:979">
P1: When a branch-protection setting is entered as `null`, required-parameter validation treats it as missing, so the documented disable request never reaches GitHub. Allow explicit null for these required JSON settings while continuing to reject omitted values.</violation>

<violation number="2" location="apps/sim/blocks/blocks/github.ts:2347">
P2: When users select `All` for `github_list_branches`, the mapper leaves `protected` as the string `all`, so the request sends an invalid boolean filter. Override this value to `undefined` to omit the query parameter, while retaining the `true`/`false` coercion.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/blocks/blocks/github.ts Outdated
Comment thread apps/sim/tools/github/list_workflow_runs.ts
Comment thread apps/sim/blocks/blocks/github.ts
Marking the four branch-protection settings `required: true` made the
documented way to switch one off unreachable.

GitHub lists all four in the endpoint's `required` array and marks every one
`nullable`: the key must be present in the body, and `null` is how a caller
disables that protection. Sim's `required` means something stricter — present
*and* not null — so `validateRequiredParametersAfterMerge` rejects `null` as a
missing parameter before the request is ever built. The placeholders told users
to type exactly the value that could not get through.

Models the real contract instead: the params are optional, and the body always
emits all four keys, sending `null` for anything unset. An omitted value cannot
simply be dropped either — `JSON.stringify` strips `undefined`, and GitHub
rejects the body for the missing key.

The subBlocks stay visible and out of advanced mode, which was the point of
surfacing them; they are just no longer required, and the placeholders now say
to leave the field blank rather than to type null.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found and verified against the latest diff

Confidence score: 3/5

  • In apps/sim/blocks/blocks/github.ts, the alias mapper can retain stale canonical values when a subBlock is blank after switching operations, causing unrelated milestone titles or descriptions to be sent; clear the mapped target when the alias is blank.
  • In apps/sim/blocks/blocks/github.ts, List Branches with All currently sends the invalid protected=all query, which can produce incorrect requests; map All to undefined so the protection filter is omitted.
  • In apps/sim/blocks/blocks/github.test.ts, the mapper test makes no assertions and provides no subBlock values, so it cannot detect either parameter-mapping regression; add assertions covering blank aliases and the All branch option.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/blocks/blocks/github.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.ts:2341">
P2: When an alias subBlock is blank after switching operations, this `continue` leaves a stale canonical value in `finalInputs`, so unrelated milestone titles or descriptions can be sent. Clear the mapped target when the alias key is present, while still skipping absent alias keys to preserve direct model-supplied params.</violation>

<violation number="2" location="apps/sim/blocks/blocks/github.ts:2348">
P2: When users select All for List Branches, the mapper leaves `'all'` as a string and the tool sends the invalid `protected=all` query. Map this option to `undefined` so the request omits the protection filter.</violation>
</file>

<file name="apps/sim/blocks/blocks/github.test.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.test.ts:127">
P3: This test never asserts anything: it calls `mapParams({ operation })` with no subBlock values, and the mapper (github.ts lines 2340-2350) skips every alias whose value is `undefined`/`null`/`''` and only writes a boolean when the value is exactly `'true'`/`'false'`. So `mapped` is `{}` for every operation and the inner `for...of` iterates zero entries, meaning the `expect(...).not.toBeUndefined()` guard can never fail regardless of implementation. Either feed it realistic values so it actually exercises a non-empty map, or remove it, since the 'supplies every required tool param' test already covers undefined emissions.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/blocks/blocks/github.ts
Comment thread apps/sim/blocks/blocks/github.ts Outdated
Comment thread apps/sim/blocks/blocks/github.test.ts Outdated
Comment thread apps/sim/tools/github/update_branch_protection.ts Outdated
The scoped workflow-runs path I added reintroduced, in one parameter, the exact
over-broad query this PR set out to fix.

`encodeURIComponent('..')` returns `'..'` verbatim — a dot segment is made
entirely of unreserved characters — and the WHATWG parser that `fetch` uses then
removes it and pops a path segment. So `workflow_id: '..'` resolved back to
`/repos/{owner}/{repo}/actions/runs` and silently listed every run in the
repository, with nothing to say the scope had been dropped. `'.'` produced a
bogus path the same way.

Only rejection closes this; no encoding scheme neutralizes a dot segment. A dot
segment *inside* a longer value is already inert and stays accepted, because its
separators survive as `%2F` and the parser does not decode those before removing
dot segments — `.github/workflows/../ci.yml` is preserved intact.

This matches `safeEncodedUrlPathSegment` in #7262 exactly, so the rebase is a
clean swap for that helper.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found and verified against the latest diff

Confidence score: 2/5

  • apps/sim/tools/github/update_branch_protection.ts can clear every branch-protection setting omitted by the caller, unintentionally disabling protections during partial updates. Preserve existing settings before constructing the PUT payload.
  • apps/sim/blocks/blocks/github.ts sends blank JSON fields as "", causing GitHub to reject updates instead of disabling those settings. Normalize blank strings to null before building the tool request.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/blocks/blocks/github.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.ts:978">
P1: When a user leaves a branch-protection JSON field blank, the request sends `""` instead of `null`, so GitHub rejects the update rather than disabling that setting. Normalize blank strings to `null` before the tool request (or make the request builder treat blank strings as null).</violation>
</file>

<file name="apps/sim/tools/github/update_branch_protection.ts">

<violation number="1" location="apps/sim/tools/github/update_branch_protection.ts:88">
P1: When a caller supplies only some branch-protection settings, this PUT sends `null` for every omitted setting and disables protections the caller did not intend to change. Preserve the existing settings before building the full replacement body, or require callers to provide the complete configuration.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/blocks/blocks/github.ts
* `undefined`, and GitHub rejects the body for the missing key.
*/
const body: Record<string, unknown> = {
required_status_checks: params.required_status_checks ?? null,

@cubic-dev-ai cubic-dev-ai Bot Aug 29, 2026

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: When a caller supplies only some branch-protection settings, this PUT sends null for every omitted setting and disables protections the caller did not intend to change. Preserve the existing settings before building the full replacement body, or require callers to provide the complete configuration.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/tools/github/update_branch_protection.ts, line 88:

<comment>When a caller supplies only some branch-protection settings, this PUT sends `null` for every omitted setting and disables protections the caller did not intend to change. Preserve the existing settings before building the full replacement body, or require callers to provide the complete configuration.</comment>

<file context>
@@ -77,11 +77,18 @@ export const updateBranchProtectionTool: ToolConfig<
+       * `undefined`, and GitHub rejects the body for the missing key.
+       */
+      const body: Record<string, unknown> = {
+        required_status_checks: params.required_status_checks ?? null,
+        enforce_admins: params.enforce_admins ?? null,
+        required_pull_request_reviews: params.required_pull_request_reviews ?? null,
</file context>
Fix with cubic

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Real, and worth stating in the tool — fixed in bc5e8df, though not the way suggested.

You're right that PUT .../protection sends all four keys and that an omitted setting is therefore disabled, not preserved. That is GitHub's design: the endpoint sets the whole configuration at once, every key is in its required list, and there is no merge form of it.

I didn't add a read-modify-write. Fetching current protection and merging would race a concurrent change (two agents each PUT a merge of a config that has since moved, and one silently reverts the other), and it would change a documented replace endpoint into something that behaves differently from every other client, including gh. Quietly preserving settings the caller didn't mention is its own surprise.

What was actually missing is that nothing said so — especially on the model path, where the description is the whole contract. The tool description now leads with it, and each of the four params says "Leave out to DISABLE … this endpoint replaces the whole configuration, it does not merge."

Worth noting this operation could not be invoked at all before this PR — restrictions was required by the API and had no field, so every call 422'd. So there's no behaviour being regressed here, only a footgun that is now documented where a caller reads it.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 14 files

Confidence score: 2/5

  • apps/sim/tools/github/update_branch_protection.ts can clear every existing branch-protection rule when a caller updates only one field, potentially weakening repository security; require explicit replacement values or fetch and merge the current protection settings.
  • apps/sim/blocks/blocks/github.ts leaves cleared protection fields as empty strings, causing GitHub to reject requests instead of disabling those rules; normalize empty values to null before sending the update.
  • apps/sim/blocks/blocks/github.test.ts does not exercise its assertion because mapParams returns an empty object, leaving the protection-parameter behavior unverified; provide a non-empty mapped input and assert the resulting values.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/tools/github/update_branch_protection.ts">

<violation number="1" location="apps/sim/tools/github/update_branch_protection.ts:88">
P1: When a caller updates only one protection rule, this `PUT` sends `null` for every omitted rule and disables the existing protections. Require an explicit value for each replacement field, or fetch and merge the current protection settings before issuing the update.</violation>
</file>

<file name="apps/sim/blocks/blocks/github.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.ts:978">
P2: When a branch-protection JSON field is cleared, the handler leaves `''` unchanged and the API request sends it instead of `null`, so GitHub rejects the update rather than disabling that rule. Normalize empty values to `null` before building the request body.</violation>
</file>

<file name="apps/sim/blocks/blocks/github.test.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.test.ts:127">
P3: This test is a no-op: `mapParams({ operation })` always returns `{}` because the mapper skips undefined/null/empty alias values and only coerces the exact strings 'true'/'false', so the inner `expect` never executes for any operation. It passes vacuously and provides false confidence that aliases never emit undefined. Remove it, or drive it with a source value for each alias so it actually asserts something.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

* `undefined`, and GitHub rejects the body for the missing key.
*/
const body: Record<string, unknown> = {
required_status_checks: params.required_status_checks ?? null,

@cubic-dev-ai cubic-dev-ai Bot Aug 29, 2026

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: When a caller updates only one protection rule, this PUT sends null for every omitted rule and disables the existing protections. Require an explicit value for each replacement field, or fetch and merge the current protection settings before issuing the update.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/tools/github/update_branch_protection.ts, line 88:

<comment>When a caller updates only one protection rule, this `PUT` sends `null` for every omitted rule and disables the existing protections. Require an explicit value for each replacement field, or fetch and merge the current protection settings before issuing the update.</comment>

<file context>
@@ -77,11 +77,18 @@ export const updateBranchProtectionTool: ToolConfig<
+       * `undefined`, and GitHub rejects the body for the missing key.
+       */
+      const body: Record<string, unknown> = {
+        required_status_checks: params.required_status_checks ?? null,
+        enforce_admins: params.enforce_admins ?? null,
+        required_pull_request_reviews: params.required_pull_request_reviews ?? null,
</file context>
Fix with cubic

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Same point Greptile raised on the adjacent line — answered in full there, and addressed in bc5e8df by documentation rather than code.

Short version: PUT .../protection is a replace endpoint by GitHub's design (all four settings are in the schema's required array and all four are nullable), so "omitted means disabled" is the contract itself. Requiring a non-null value for each is exactly the bug the previous commit fixed — it makes disabling unreachable. Fetching and merging races a concurrent change and diverges from every other client of the endpoint.

The tool and per-param descriptions now say plainly that the call replaces the whole configuration and that an omitted setting is disabled. See the neighbouring thread for the full reasoning and for the enforcement shape I'd pick if you want it in code instead.

Comment thread apps/sim/blocks/blocks/github.ts
Comment thread apps/sim/blocks/blocks/github.test.ts Outdated
Blank fields, a tri-state filter, and stale block state each defeated the
mapper in a different way.

**A blank field disabled nothing.** A cleared `short-input` arrives as `''`, and
the handler's JSON coercion only parses non-blank strings, so the blank reached
the branch-protection body verbatim and GitHub rejected `""` where it expects an
object or null. Blank now means null, which is what the placeholder promises.

**A stale sibling could be sent.** The guard that stopped the mapper clobbering
a model-supplied param also stopped it clearing one. Block state keeps values
for fields the current operation does not render, so a leftover `title` from
Create Issue could reach Update Milestone when its own field was blank. The rule
is now about presence, not truthiness: an absent alias key means the caller
addressed the tool param directly and is left alone; a key present but blank
means the operator cleared the field and the target is cleared with it.

**"All" was sent as a filter.** `protected` is a tri-state in the UI and a
boolean on the wire, and `list_branches` appends the filter whenever it is not
undefined — so the sentinel went out as the invalid `protected=all`. It now
becomes an omission.

**Replace semantics were not stated.** `PUT .../protection` sets the whole
configuration at once, so omitting a setting disables it. That is GitHub's
design and not something the tool can merge away without a read-modify-write
that would race a concurrent change, but a caller — especially a model — has to
be told. The tool and per-param descriptions now say it plainly.

Also replaces a test that could not fail: it asserted over a map that was always
empty, so it never exercised the mapper. The presence rule it was meant to
protect is now pinned by two tests that distinguish an absent alias key from a
blank one.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 15 files

Confidence score: 3/5

  • In apps/sim/tools/github/list_workflow_runs.ts, an invalid present workflow_id such as true can broaden the request from one workflow to all runs, creating a concrete correctness and potentially costly data-scope risk; reject invalid values instead of treating them as omitted.
  • In apps/sim/tools/github/list_workflow_runs.ts, trimming a valid workflow filename can alter its identifier and cause the wrong workflow lookup or a 404; preserve the raw filename while using trimming only to detect omission.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/tools/github/list_workflow_runs.ts">

<violation number="1" location="apps/sim/tools/github/list_workflow_runs.ts:91">
P2: When `workflow_id` is present with an invalid runtime type such as `true`, this fallback silently widens the request to every workflow run. Reject invalid present values instead of treating them as an omitted workflow.</violation>

<violation number="2" location="apps/sim/tools/github/list_workflow_runs.ts:93">
P2: When a valid workflow filename contains significant surrounding whitespace, `.trim()` changes the identifier and can query the wrong workflow or return 404. Preserve the raw filename, using trimming only to detect omission if that behavior is required.

(Based on your team's feedback about preserving whitespace in path identifiers.)</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

*/
const workflowId =
typeof params.workflow_id === 'string' || typeof params.workflow_id === 'number'
? String(params.workflow_id).trim()

@cubic-dev-ai cubic-dev-ai Bot Aug 29, 2026

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: When a valid workflow filename contains significant surrounding whitespace, .trim() changes the identifier and can query the wrong workflow or return 404. Preserve the raw filename, using trimming only to detect omission if that behavior is required.

(Based on your team's feedback about preserving whitespace in path identifiers.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/tools/github/list_workflow_runs.ts, line 93:

<comment>When a valid workflow filename contains significant surrounding whitespace, `.trim()` changes the identifier and can query the wrong workflow or return 404. Preserve the raw filename, using trimming only to detect omission if that behavior is required.

(Based on your team's feedback about preserving whitespace in path identifiers.) </comment>

<file context>
@@ -76,9 +83,31 @@ export const listWorkflowRunsTool: ToolConfig<ListWorkflowRunsParams, ListWorkfl
+       */
+      const workflowId =
+        typeof params.workflow_id === 'string' || typeof params.workflow_id === 'number'
+          ? String(params.workflow_id).trim()
+          : ''
+      /**
</file context>
Fix with cubic

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Deliberate here, and I want to explain rather than just decline — the learning you're applying is real, but it belongs to the other half of a split this repo makes on purpose.

#7262 introduces three path helpers precisely because these are two different kinds of value:

  • safeUrlPath — a real multi-segment file path, and it trims nowhere. A leading or trailing space is a legal git filename (docs/my file .txt is tracked verbatim), so trimming would silently address a different file. That is the feedback you're citing, and it is correct for path on update_file/delete_file.
  • safeUrlPathSegment / safeEncodedUrlPathSegment — one opaque, copy-pasted identifier, and both trim, because surrounding whitespace there is transport noise.

workflow_id is in the second group. #7262 guards this exact parameter with safeUrlPathSegment, which trims, and the encoded variant I mirror here trims too. Keeping .trim() is what makes this a clean swap for the shared helper on rebase; dropping it would put this one call site out of step with every other identifier in the integration.

The residual risk is a workflow file whose name has significant leading or trailing whitespace. GitHub Actions only reads workflows from .github/workflows/, and such a name would be pathological rather than merely unusual — against a copy-pasted id or filename picking up a stray space, which is routine. If the trimming rule for identifiers is ever revisited, it should change in url-path.ts for all of them at once rather than diverge here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Deliberate here — and the learning you're applying is real, it just belongs to the other half of a split this repo makes on purpose.

#7262 introduces three path helpers precisely because these are two different kinds of value:

  • safeUrlPath — a real multi-segment file path, which trims nowhere. A leading or trailing space is a legal git filename (docs/my file .txt is tracked verbatim), so trimming would silently address a different file. That is the feedback you're citing, and it is correct for path on update_file/delete_file.
  • safeUrlPathSegment / safeEncodedUrlPathSegment — one opaque, copy-pasted identifier. Both trim, because whitespace around an id is transport noise.

workflow_id is in the second group: #7262 guards this exact parameter with safeUrlPathSegment, and the encoded variant I mirror here trims too. Keeping .trim() is what makes this a clean swap for the shared helper on rebase; dropping it would put this one call site out of step with every other identifier in the integration.

The residual risk is a workflow file whose name carries significant leading or trailing whitespace. Actions only reads workflows from .github/workflows/, so such a name would be pathological rather than merely unusual — weighed against a copy-pasted id or filename picking up a stray space, which is routine. If the trimming rule for identifiers is revisited it should change in url-path.ts for all of them at once, not diverge here.

Comment thread apps/sim/tools/github/list_workflow_runs.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 15 files

Confidence score: 5/5

  • In apps/sim/blocks/blocks/github.test.ts, visibleSubBlocks may omit sub-blocks when the operation condition is nested under and:, causing the test helper to disagree with what the editor renders; update the condition matching to handle nested clauses.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/blocks/blocks/github.test.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.test.ts:31">
P3: The `visibleSubBlocks` helper is documented as returning "The subBlocks the editor renders once operation is chosen", but it only evaluates the top-level `condition.field === 'operation'` match and drops the `and:` sub-condition. In `github.ts`, the `path` and `line` subBlocks (github.ts:1945-1980) are gated by `condition: { field: 'operation', value: 'github_comment', and: { field: 'commentType', value: 'file_comment' } }`, so the helper treats them as visible for the `github_comment` operation even though the editor only renders them when `commentType` is `file_comment`. The cast type also omits `and`, so TypeScript won't catch this. Since `resolveToolInputs` fills every over-included subBlock, this central regression guard can only over-report inputs — it cannot catch a required tool param that is only reachable through an `and:`-gated subBlock the operator isn't shown, which is exactly the class of inert-input bug this test exists to prevent. Extend the helper to evaluate the `and` clause (and honor `mode: 'advanced'`/`showWhenEnvSet`/`hideFromCopilot`) so the guard models what the editor actually renders.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/blocks/blocks/github.test.ts Outdated
… query

A present value of the wrong kind fell through to the omission branch, so
`workflow_id: true` silently dropped the scope and listed every run in the
repository — the same over-broad query as the dot segment, reached by a
different route.

A value the caller did supply must never be read as one they did not. A
non-string, non-number value now fails by name; `undefined` and `null` still
mean "list the whole repository", which is the documented behaviour of omitting
the parameter.

This mirrors `toGuardedString` in #7262, so the rebase onto the shared helper
stays a clean swap.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 15 files

Confidence score: 4/5

  • apps/sim/tools/github/types.ts: ListWorkflowRunsParams cannot represent numeric workflow IDs even though the tool accepts and documents them, which can block typed callers from using valid IDs; type workflow_id as string | number.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/tools/github/types.ts">

<violation number="1" location="apps/sim/tools/github/types.ts:1525">
P2: Numeric workflow IDs cannot be represented by `ListWorkflowRunsParams`, even though this tool documents and accepts them. Type `workflow_id` as `string | number` so typed callers can use numeric IDs.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/tools/github/types.ts Outdated
…ator

The reachability guard hand-rolled its condition match and only looked at the
top-level `operation` field, so it silently ignored `and:`. A compound-gated
subBlock — `github_comment`'s `path` and `line`, which also need
`commentType: 'file_comment'` — was therefore counted as visible under states
where the editor does not render it, and the cast's type omitted `and`, so
TypeScript could not flag the gap.

That defect only ever over-reports available inputs, which is exactly the false
confidence this file exists to remove: the guard could pass while a required
param had no reachable field.

Uses `evaluateSubBlockCondition` instead, satisfying the secondary gate before
evaluating. "Can be rendered" is the right question for a reachability guard —
whether an operation has any way to supply a required param, not whether one
particular editor state happens to show it — and the helper now says so.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 15 files

Confidence score: 3/5

  • In apps/sim/blocks/blocks/github.ts, the operation switch can leave the alias unset while a stale canonical value from another operation is still sent, potentially applying the wrong request parameter; filter persisted inputs to the selected operation or otherwise distinguish unset values before building the request.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/blocks/blocks/github.ts">

<violation number="1" location="apps/sim/blocks/blocks/github.ts:2351">
P2: When an operation switch leaves the alias field unset, this guard lets a stale canonical value from another operation reach the request. Filter persisted inputs to fields for the selected operation, or otherwise distinguish block-state inputs from direct tool inputs before preserving the canonical value.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

* same-named leftover (`title` from Create Issue, `sort` from a
* search) would otherwise be sent under this operation.
*/
if (!(subBlockId in params)) continue

@cubic-dev-ai cubic-dev-ai Bot Aug 29, 2026

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: When an operation switch leaves the alias field unset, this guard lets a stale canonical value from another operation reach the request. Filter persisted inputs to fields for the selected operation, or otherwise distinguish block-state inputs from direct tool inputs before preserving the canonical value.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/blocks/blocks/github.ts, line 2351:

<comment>When an operation switch leaves the alias field unset, this guard lets a stale canonical value from another operation reach the request. Filter persisted inputs to fields for the selected operation, or otherwise distinguish block-state inputs from direct tool inputs before preserving the canonical value.</comment>

<file context>
@@ -2279,6 +2329,44 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
+           * same-named leftover (`title` from Create Issue, `sort` from a
+           * search) would otherwise be sent under this operation.
+           */
+          if (!(subBlockId in params)) continue
+          const value = params[subBlockId]
+          mapped[toolParam] = value === '' || value === null ? undefined : value
</file context>
Fix with cubic

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thought about this one for a while. The blank case you raised earlier is fixed; this residual is genuinely undecidable at this layer, and I'd rather say so than paper over it.

From the mapper's inputs, these two are indistinguishable:

  • UI path: operator switched to Update Milestone, milestone_title untouched, a stale title lingers from Create Issue.
  • Agent path: the model addressed the tool param directly as title, because the tool param name is what the LLM schema exposes.

Both arrive as "alias key absent, canonical key present". Clearing unconditionally would fix the first and break the second — and break it hardest exactly where it hurts most: content is required on both reaction tools, so dropping a model-supplied content fails the call outright. That's a worse, more frequent regression than the stale value.

What I think saves the reachable case: a subBlock the editor has rendered is in block state, so under Update Milestone milestone_title is present (blank) rather than absent, and the presence rule then clears the stale title. The leak needs milestone_title absent while title is present — the operation selected without its own form ever being rendered.

Your suggested fix — scoping persisted inputs to the active operation — is the right one, but it belongs upstream in the executor rather than in one integration's mapper: every block with per-operation fields has this shape, and solving it here would leave the other 250 untouched while adding a second, divergent notion of visibility. Happy to raise it against generic-handler if you agree that's the right home.

Leaving as-is deliberately, with the tradeoff recorded here rather than silently chosen.

Comment thread apps/sim/tools/github/types.ts Outdated
The request builder accepts a number and stringifies it — an LLM tool call
serializes a numeric id as a JSON number — but the param type said `string`, so
a typed caller could not pass what the runtime documents and handles.

Widened to `string | number`, and the test that covers the numeric form now goes
through the declared type so the two cannot drift apart again.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 15 files

Confidence score: 3/5

  • In apps/sim/tools/github/types.ts, the LLM tool path still declares workflow_id as a string, so callers cannot use the documented numeric form when invoking list_workflow_runs; update the tool schema to represent numeric support.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/tools/github/types.ts">

<violation number="1" location="apps/sim/tools/github/types.ts:1530">
P2: When `list_workflow_runs` is invoked through the LLM tool path, the numeric support documented here is not exposed because the tool schema still declares `workflow_id` as a string. Update the tool schema to represent the accepted string-or-number contract, or keep this interface string-only and remove the numeric guarantee.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

* call serializes a numeric id as a JSON number, and the request builder
* stringifies it.
*/
workflow_id?: string | number

@cubic-dev-ai cubic-dev-ai Bot Aug 29, 2026

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: When list_workflow_runs is invoked through the LLM tool path, the numeric support documented here is not exposed because the tool schema still declares workflow_id as a string. Update the tool schema to represent the accepted string-or-number contract, or keep this interface string-only and remove the numeric guarantee.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/tools/github/types.ts, line 1530:

<comment>When `list_workflow_runs` is invoked through the LLM tool path, the numeric support documented here is not exposed because the tool schema still declares `workflow_id` as a string. Update the tool schema to represent the accepted string-or-number contract, or keep this interface string-only and remove the numeric guarantee.</comment>

<file context>
@@ -1521,6 +1521,13 @@ export interface TriggerWorkflowParams extends BaseGitHubParams {
+   * call serializes a numeric id as a JSON number, and the request builder
+   * stringifies it.
+   */
+  workflow_id?: string | number
   actor?: string
   branch?: string
</file context>
Fix with cubic

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Deliberate, and it mirrors an existing pattern in the repo rather than an oversight — but you're right that the two lines should agree on what they each mean, so I want to state it.

The tool type is a single JSON-schema scalar; there is no union to declare, so 'string' is the only accurate schema for a parameter GitHub documents as "the ID of the workflow — you can also pass the workflow file name as a string". Declaring 'number' instead would be worse: it would push the LLM away from the filename form, which is the one list_workflows actually surfaces via path.

The TypeScript widening is not a promise made to the model — it's tolerance for a value that arrives as a number regardless of what the schema says. #7262's toGuardedString documents exactly this, and it's the reason that helper accepts numbers at all:

Tool params are declared type: 'string', but that declaration is not enforced anywhere before the value reaches here: it arrives from an LLM tool call or from stored workflow state, where a numeric-looking id can be serialized as a JSON number and stays one.

So 'string' in the schema and string | number in the interface are describing two different things: what the model is asked for, and what the runtime must survive. Removing the numeric tolerance would reintroduce the failure that reasoning exists to prevent — and a numeric id sent as 42 would take the '' branch and silently widen the query to the whole repository, which is the bug I fixed two commits ago.

The interface comment now says this explicitly, so the next reader doesn't have to infer it.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

@cubic review

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 15 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Thread status: 15 resolved, 5 deliberately open

Resolved the threads where a fix actually landed, and left open the ones where I declined — those are live disagreements and the reviewer should get the last word rather than have me close my own argument.

Open on purpose (5)

Branch-protection replace semantics — two threads on update_branch_protection.ts.
PUT .../protection is a replace endpoint by GitHub's design: all four settings are in the schema's required array and all four are nullable, so "omitted means disabled" is the endpoint's contract. Requiring non-null values is exactly the bug commit 406c3a4 fixed — it makes disabling unreachable. Read-modify-write races a concurrent change and diverges from every other client of the endpoint. Addressed by documentation (tool + per-param descriptions now state it plainly) rather than code. If you'd rather have it enforced, the shape I'd pick is requiring all four keys present while allowing each to be null; I left it out because I can't verify from tests alone whether an untouched subBlock arrives as '' or absent.

Trimming workflow_idlist_workflow_runs.ts.
The no-trim rule belongs to safeUrlPath (real file paths, where a leading space is a legal filename), not to identifiers. #7262 guards this exact parameter with the trimming helper. Keeping .trim() is what makes the rebase a clean swap.

Stale canonical value when the alias field is never renderedgithub.ts.
Genuinely undecidable at this layer: "alias key absent, canonical key present" is identical for a stale UI value and a direct model supply. Clearing unconditionally would drop a model-supplied content, which is required on both reaction tools. The blank case — the one reachable in the editor — is fixed. The real fix is scoping persisted inputs to the active operation in generic-handler, which belongs upstream rather than in one integration's mapper.

Tool schema 'string' vs interface string | numbertypes.ts.
Two different contracts: what the model is asked for (a single JSON scalar; 'number' would push the LLM away from the filename form) versus what the runtime must survive (an LLM tool call serializes a numeric id as a JSON number). Removing the tolerance would reintroduce the silent-widening bug fixed in 5040596.

Also confirmed for the rebase

Only one identifier in this diff is newly trimmed — workflow_id — and list_workflow_runs is a GET, so it stays on the trimming helper rather than #7262's strictUrlPathSegment, which refuses padded identifiers on state-changing requests. No mutating request here interpolates a newly-trimmed identifier: check_star is a GET with its URL untouched, and update_branch_protection's URL is untouched in this diff (its .trim() is a blankness test on a body field, returning the original value unmodified).

The PR body now also records the two findings worth carrying forward: the workflow_id: '..' dot-segment bug (encoding mistaken for protection, inside the PR fixing that class) and the vacuous-assertion test.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Closing for now — not because of a defect. This batch grew to 17 PRs across ~700 changed call sites, and we would rather revisit it as smaller, independently testable pieces than merge this much at once.

Nothing here is lost: the branch fix/github-block-param-wiring is preserved and this PR can be reopened. Review state, the reasoning on every thread, and the red-first verification all stay attached.

@waleedlatif1
waleedlatif1 deleted the fix/github-block-param-wiring branch August 29, 2026 07:16
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.

1 participant