Skip to content
This repository was archived by the owner on May 30, 2026. It is now read-only.

fix: accept null in WebhookPayload IssueRef and Repository string fields - #135

Merged
C. Spencer Beggs (spencerbeggs) merged 2 commits into
mainfrom
feat/webhook-payload
May 28, 2026
Merged

C. Spencer Beggs (spencerbeggs) merged 2 commits into
mainfrom
feat/webhook-payload

Conversation

@spencerbeggs

Copy link
Copy Markdown
Contributor

Summary

ActionEnvironment.payload failed to decode real pull_request events because the WebhookPayload schema declared IssueRef.body, IssueRef.html_url, Repository.full_name, and Repository.html_url as Schema.optional(Schema.String) — string-or-undefined. GitHub webhook payloads carry these fields as null (not absent) when the issue or PR has no description, or when the event payload omits the rendered URL.

Switch the four affected optional fields to Schema.optional(Schema.NullOr(Schema.String)).

Repro

Job that exposed it: https://github.com/savvy-web/rslib-builder/actions/runs/26596426144/job/78380850728

Calling savvy-web/silk-router-action@v1 (which uses @savvy-web/github-action-effects ^2.0.0) on a pull_request event with pull_request.body = null triggered:

[ActionEnvironmentError] Event payload did not match the expected shape: WebhookPayload
└─ ["pull_request"]
   └─ { readonly number: number; readonly html_url?: string | undefined; readonly body?: string | undefined } | undefined
      ├─ { readonly number: number; readonly html_url?: string | undefined; readonly body?: string | undefined }
      │  └─ ["body"]
      │     └─ string | undefined
      │        ├─ Expected string, actual null
      │        └─ Expected undefined, actual null

Test plan

  • New tests in src/schemas/EventPayload.test.ts cover both the pull_request and repository paths.
  • pnpm test — 1244 tests pass.
  • pnpm typecheck passes.
  • pnpm lint clean.
  • pnpm ci:build succeeds.

Changeset

patch in .changeset/webhook-payload-null-fields.md.

Signed-off-by: C. Spencer Beggs spencer@savvyweb.systems

Signed-off-by: C. Spencer Beggs <spencer@savvyweb.systems>
GitHub webhook payloads carry body, html_url, and full_name as null when the issue or PR has no description.

The schema previously only allowed string or undefined, so ActionEnvironment.payload failed on real pull_request events.

Switch the affected optional fields to optional NullOr String.

Signed-off-by: C. Spencer Beggs <spencer@beggs.codes>
@savvy-web-bot

savvy-web-bot Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Code Review

Current Commit: c096e2e

Summary

Clean, targeted fix. The schema change is correct, the tests directly reproduce the bug, and all five validation checks pass.

What Changed

The PR switches four Schema.optional(Schema.String) fields to Schema.optional(Schema.NullOr(Schema.String)):

Struct Field
IssueRef body
IssueRef html_url
Repository full_name
Repository html_url

This correctly models all three states GitHub can send: absent (undefined), present-but-null, and a string value. The previous schema had no way to represent the null case, causing decode failures on real pull_request events.

Validation

Check Status
Tests ✅ success
Code Quality ✅ success
Conventional Commits ✅ success
PR Title Validation ✅ success
Markdown ✅ success

Observations

Schema fix: Correct approach. Schema.NullOr wrapping the inner Schema.String before passing to Schema.optional is the right composition. The decoded TS type string | null | undefined faithfully represents what GitHub actually sends.

Tests: Both new test cases are well-structured — they pass null explicitly and assert .toBeNull() rather than .toBeFalsy(), which correctly verifies that null survives decoding rather than being coerced to undefined. The required non-nullable fields (number, name, owner.login) are included in each fixture.

@remarks on IssueRef: The new JSDoc block explains why null is accepted. Worth noting that Repository has the same null-accepting fields but no equivalent documentation — minor nit, not a blocker.

Ancillary changes: The workflow-runtime-action@main → silk-runtime-action@v1 workflow update pins the action to a versioned tag instead of the mutable @main ref, which is a security/stability improvement. The test fixture renames (workflow-release-action → silk-release-action) are correct housekeeping.

Patch changeset: Correct bump level — this is a backwards-compatible bug fix. Consumers previously failing on null payloads will now succeed; no existing consumers are broken.


No actionable issues. Approving.

@savvy-web-bot savvy-web-bot Bot left a comment

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.

Issues addressed at c096e2e; approving.

@spencerbeggs
C. Spencer Beggs (spencerbeggs) merged commit 56ea730 into main May 28, 2026
24 of 26 checks passed
@spencerbeggs
C. Spencer Beggs (spencerbeggs) deleted the feat/webhook-payload branch May 28, 2026 20:40
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant