gh shim: typed -F field=@file on governed api writes reads the file

Merged
AF
1d ago
+534-145

A governed gh api comment edit with a typed file field, gh api -X PATCH repos/<o>/<r>/issues/comments/<id> -F body=@/tmp/file.md, posted the literal text @/tmp/file.md as the comment body, replacing real comments (reported by MC on cortexkit/magic-context).

  • Cause: a legacy own-comment PATCH decoder (own_comment_patch_payload / api_payload_argument in gh_shim.rs) treated typed -F and raw -f fields the same, so it never read @file. The shared REST payload parser already read it correctly.
  • Fix: that duplicate decoder is gone, and legacy PATCH goes through the shared parser. Typed fields follow upstream gh (cli/cli pkg/cmd/api/fields.go and iostreams.go): @path reads the file, @- reads stdin, and file contents aren't retyped. Raw -f body=@literal stays literal. For prose-only governed fields, a typed scalar (true, false, null, an integer) is refused by field name instead of being stringified. Unrepresentable mixes (--input plus fields, typed {owner} placeholders, repeated typed @-) are refused before anything is sent.
  • Refusal text: existing texts reused: could not read body file {path}: {error} and could not read body from stdin: {error}.
  • Audit: every governed REST/GraphQL row and every governed CLI body or file flag is inventoried with coverage. Real-binary relay goldens now cover PATCH and POST with typed file and stdin fields, raw fields, missing or unreadable files, JSON input, scalar refusal and mixed-input refusal, plus file, stdin and missing-file cases for seven governed CLI body commands.
  • --jq on governed writes stays unsupported. Upstream filters locally with embedded gojq, so matching it is a separate job.

A test-only commit reproduces the corruption first (the golden showed the literal path as the body). Verified on the build server: 548 gh_shim library tests, 101 serial gh_shim end-to-end tests, and the Windows target check.


Summary by cubic

A governed gh api write with a typed file field now reads the file, so a comment edit like -F body=@/tmp/file.md posts the file contents instead of the literal path. The duplicate legacy comment-PATCH decoder that treated -F and -f identically is removed; those writes go through the shared REST payload parser.

Typed-field behavior now matches upstream gh

  • Typed fields read @path files and @- stdin; -f stays literal, and file contents aren't retyped.
  • Typed scalars (true, false, null, integers) on prose-only fields are refused rather than stringified.
  • --input combined with fields, typed {owner}-style placeholders, and repeated @- are refused before anything is sent.
  • Stack pull_requests[] accepts only typed JSON integers; string numbers now refuse instead of being silently coerced.