Repository: cli/cli (Go)

Issue:
gh project item-edit command fails with value for --number is floating point
### Describe the bug

I'm trying to issue the gh project item-edit command to set the value of an item field to a floating point value. The command looks like this:

```
gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 69.84 
```

The command fails with this error:
> GraphQL: Number values cannot exceed 8 decimal places (updateProjectV2ItemFieldValue)

which would be helpful if the value I passed had more than 8 decimal places, but it doesn't.

### Affected version

Please run `gh version` and paste the output below.

gh version 2.65.0 (2025-01-06)

### Steps to reproduce the behavior

- Create a project a define a number field in it.
- Add an item to the project
- Figure out the project ID, item ID, and field ID (no small feat).
- Issue the command above.

### Expected vs actual behavior

The update should succeed when the value provided is valid.

### Logs

Paste the activity from your command line. Redact if needed.

<!-- Note: Set `GH_DEBUG=true` for verbose logs or `GH_DEBUG=api` for verbose logs with HTTP traffic details. -->

[mikekistler@macbookpro] ~>export GH_DEBUG=true                                                                        
[mikekistler@macbookpro] ~>gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 69.84
* Request at 2025-01-30 21:57:28.393059 -0600 CST m=+0.122595376
* Request to https://api.github.com/graphql
* Request took 379.058041ms
GraphQL: Number values cannot exceed 8 decimal places (updateProjectV2ItemFieldValue)
[mikekistler@macbookpro] ~>



Discussion:
Thanks for opening up this issue, @mikekistler! 🙇 Instead of `GH_DEBUG=true`, would you be able to post `GH_DEBUG=api` results instead?

My knee jerk is that somehow this number is being manipulated before being sent to the GitHub API, but let's see what ☝ yields.
@andyfeller That was a good guess!

```text
[mikekistler@macbookpro] ~>export GH_DEBUG=api
[mikekistler@macbookpro] ~>gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 69.84
* Request at 2025-02-01 11:03:20.039991 -0600 CST m=+0.133945001
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token xxxxx
> Content-Length: 3191
> Content-Type: application/json
> Graphql-Features: merge_queue
> Time-Zone: America/Chicago
> User-Agent: GitHub CLI 2.65.0

GraphQL query:
mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
GraphQL variables: {"input":{"projectId":"PVT_kwDOAIt-yc4ArJar","itemId":"PVTI_lADOAIt-yc4ArJarzgWylsY","fieldId":"PVTF_lADOAIt-yc4ArJarzgnNyeQ","value":{"number":69.83999633789062}}}

< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Sat, 01 Feb 2025 17:03:20 GMT
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: repo
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: C8D7:F0029:D1CE19:1A83425:679E53D8
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: gist, project, read:org, repo, workflow
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 4975
< X-Ratelimit-Reset: 1738429765
< X-Ratelimit-Resource: graphql
< X-Ratelimit-Used: 25
< X-Xss-Protection: 0

{
  "data": {
    "updateProjectV2ItemFieldValue": null
  },
  "errors": [
    {
      "type": "VALIDATION",
      "path": [
        "updateProjectV2ItemFieldValue"
      ],
      "locations": [
        {
          "line": 1,
          "column": 71
        }
      ],
      "message": "Number values cannot exceed 8 decimal places"
    }
  ]
}

* Request took 301.1675ms
GraphQL: Number values cannot exceed 8 decimal places (updateProjectV2ItemFieldValue)
```
@andyfeller: Looked into it.
Apparently, it's related to conversion from `float32` to `float64`.

From:
https://github.com/cli/cli/blob/fab84beb900c1789076cd7b538bb05739d41cea6/pkg/cmd/project/item-edit/item_edit.go#L26

To:
https://github.com/cli/cli/blob/fab84beb900c1789076cd7b538bb05739d41cea6/pkg/cmd/project/item-edit/item_edit.go#L177

Where, `githubv4.Float` is `graphql.Float`:
https://github.com/shurcooL/githubv4/blob/48295856cce734663ddbd790ff54800f784f3193/scalar.go#L37C2-L37C7

and, `graphql.Float` is `float64`:
https://github.com/shurcooL/graphql/blob/ed46e5a4646634fc16cb07c3b8db389542cc8847/scalar.go#L18

Ran this simple test in [go playground](https://go.dev/play/p/drZCWrfCUuS):

```go
package main

import "fmt"

func main() {
	var n float32 = 69.84
	fmt.Println(n)
	fmt.Println(float64(n))
}
```

and, the output confirms it:

```shell
69.84
69.83999633789062
```

Given above, fixing the type of `number` field from `float32` to `float64` should fix this issue.
Thanks for the follow up, @mikekistler, and the confirmation, @iamazeem!

## Confirmation

From GraphQL API docs:

- [`UpdateProjectV2ItemFieldValueInput` input](https://docs.github.com/en/graphql/reference/input-objects#updateprojectv2itemfieldvalueinput) used for [`updateProjectV2ItemFieldValue` mutation](https://docs.github.com/en/graphql/reference/mutations#updateprojectv2itemfieldvalue)

- `value` field is [`Float`](https://docs.github.com/en/graphql/reference/scalars#float)

  > Represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).

Checking out #10374, I can confirm that this appears to address the `gh project item-set` issue.

<details>
<summary>
  <b>Reproducing this on private project</b>
</summary>
<p>

```shell
andyfeller@Andys-MBP:cli/cli ‹10342-gh-project-item-edit-number›$ GH_DEBUG=api ./bin/gh project item-edit --project-id PVT_kwDOBWmBn84Al2QA --id PVTI_lADOBWmBn84Al2QAzgXCZ4c --field-id PVTF_lADOBWmBn84Al2QAzgnpmgo --number 69.84
* Request at 2025-02-05 08:36:37.41416 -0500 EST m=+0.103935210
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Length: 3191
> Content-Type: application/json
> Graphql-Features: merge_queue
> Time-Zone: America/New_York
> User-Agent: GitHub CLI v2.66.1-72-g64b63f61

GraphQL query:
mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}},iterationId},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
GraphQL variables: {"input":{"projectId":"PVT_kwDOBWmBn84Al2QA","itemId":"PVTI_lADOBWmBn84Al2QAzgXCZ4c","fieldId":"PVTF_lADOBWmBn84Al2QAzgnpmgo","value":{"number":69.84}}}

< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Wed, 05 Feb 2025 13:36:38 GMT
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: repo
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: CBEE:3A0197:7ACD12:F5F721:67A36965
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: admin:org, admin:public_key, admin:ssh_signing_key, gist, project, repo, workflow
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 5000
< X-Ratelimit-Reset: 1738765759
< X-Ratelimit-Resource: graphql
< X-Ratelimit-Used: 202
< X-Xss-Protection: 0

{
  "data": {
    "updateProjectV2ItemFieldValue": {
      "projectV2Item": {
        "content": {
          "__typename": "Issue",
          "body": "This is the body",
          "title": "This is a title",
          "number": 37,
          "url": "https://github.com/tinyfists/gh-nonsense-internal/issues/37",
          "repository": {
            "nameWithOwner": "tinyfists/gh-nonsense-internal"
          }
        },
        "id": "PVTI_lADOBWmBn84Al2QAzgXCZ4c",
        "fieldValues": {
          "nodes": [
            {
              "__typename": "ProjectV2ItemFieldRepositoryValue",
              "repository": {
                "url": "https://github.com/tinyfists/gh-nonsense-internal"
              },
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgd1mYU",
                "name": "Repository",
                "dataType": "REPOSITORY"
              }
            },
            {
              "__typename": "ProjectV2ItemFieldTextValue",
              "text": "This is a title",
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgd1mX8",
                "name": "Title",
                "dataType": "TITLE"
              }
            },
            {
              "__typename": "ProjectV2ItemFieldNumberValue",
              "number": 69.84,
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgnpmgo",
                "name": "Floater",
                "dataType": "NUMBER"
              }
            }
          ]
        }
      }
    }
  }
}

* Request took 701.657708ms
Edited item "This is a title"
```

</p>
</details> 

## How big of a problem is this?

> Given above, fixing the type of number field from float32 to float64 should fix this issue.

Looking for [other places the GitHub CLI is working with floats](https://github.com/search?q=repo%3Acli%2Fcli%20path%3A%2F%5Epkg%5C%2Fcmd%5C%2Fproject%5C%2F%2F%20float&type=code) seems like there is a little more to this than the proposed changes:

https://github.com/cli/cli/blob/06c5934bbfffc802f367bea308a669885e09bef8/pkg/cmd/project/shared/queries/queries.go#L252-L255

I'm working to trace this to the various commands that pull `Number` fields like this out of the API to see how this 32-bit float comes into play, too.

https://github.com/cli/cli/blob/06c5934bbfffc802f367bea308a669885e09bef8/pkg/cmd/project/item-list/item_list.go#L95-L97

Once I do, then I'll write up acceptance criteria to unblock contributions.
Wrapping up my notes from digging into this a little more before separate recommendations to move this forward.

### Questions

1. **Is there any reason not to change this other reference to 64-bit float?**

   As explained below, there is 1 more reference that feels like we should be consistent in making this change.

1. **Should we validate that the `--number` provided is between `2147483647` and `-2147483647`?** 

  As explained below, it appears this isn't really a 64-bit float, so preventing users with argument validation rather than failing API calls seems like the way to go.

### `gh project item-list --format json` uses 32-bit float

The only other reference to 32-bit floats in `gh project` commandset involves exporting project items along with their field values in `gh project item-list --format json`.

Though the GraphQL documentation states that Floats are 64-bit double precision, it appears that `Number` fields for Projects don't exactly measure up.  Essentially, `Number` field values pulled out of the API are roughly 32-bit floats with the following limitations:

- largest integer value: `2147483647`
- maximum decimal digit precision: `8`

I'm working with the Projects team to update documentation to help Projects users broadly.

### Understanding double float conversions within `gh` and server side

While exploring this problem further, I was doing some manual boundary testing to understand how the API endpoints behaved.

It seems that `spf13/pflag` or Go is reading flag values and rounding it to 7 decimal places on the client side, which raises the question if we need to ensure `gh` keeps up to 8 decimal places. 🤔 

UI testing setting a `Number` field does show users an error when it exceed range, but not when the maximum precision is exceeded.

![Screenshot demonstrating error from maximum integer values exceeded](https://github.com/user-attachments/assets/382f9533-6452-4a5e-90fb-99467b74b765)

![Screenshot demonstrating no error from project UI when maximum precision exceeded](https://github.com/user-attachments/assets/7cf23d17-c921-4ecb-842e-2ad79c66cb3d)

<details>
<summary>
  <b><code>--number 2147483646.987654321</code> converts to <code>2147483646.9876542</code></b>
</summary>
<p>

```shell
andyfeller@Andys-MBP:cli/cli ‹10342-gh-project-item-edit-number›$ GH_DEBUG=api ./bin/gh project item-edit --project-id PVT_kwDOBWmBn84Al2QA --id PVTI_lADOBWmBn84Al2QAzgXCZ4c --field-id PVTF_lADOBWmBn84Al2QAzgnpmgo --number 2147483646.987654321 
* Request at 2025-02-05 09:14:43.651574 -0500 EST m=+0.077929709
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Length: 3204
> Content-Type: application/json
> Graphql-Features: merge_queue
> Time-Zone: America/New_York
> User-Agent: GitHub CLI v2.66.1-72-g64b63f61

GraphQL query:
mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}},iterationId},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
GraphQL variables: {"input":{"projectId":"PVT_kwDOBWmBn84Al2QA","itemId":"PVTI_lADOBWmBn84Al2QAzgXCZ4c","fieldId":"PVTF_lADOBWmBn84Al2QAzgnpmgo","value":{"number":2147483646.9876542}}}

< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Wed, 05 Feb 2025 14:14:44 GMT
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: repo
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: CFBD:2888E6:9F15BC:13EF257:67A37253
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: admin:org, admin:public_key, admin:ssh_signing_key, gist, project, repo, workflow
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 5000
< X-Ratelimit-Reset: 1738765759
< X-Ratelimit-Resource: graphql
< X-Ratelimit-Used: 630
< X-Xss-Protection: 0

{
  "data": {
    "updateProjectV2ItemFieldValue": {
      "projectV2Item": {
        "content": {
          "__typename": "Issue",
          "body": "This is the body",
          "title": "This is a title",
          "number": 37,
          "url": "https://github.com/tinyfists/gh-nonsense-internal/issues/37",
          "repository": {
            "nameWithOwner": "tinyfists/gh-nonsense-internal"
          }
        },
        "id": "PVTI_lADOBWmBn84Al2QAzgXCZ4c",
        "fieldValues": {
          "nodes": [
            {
              "__typename": "ProjectV2ItemFieldRepositoryValue",
              "repository": {
                "url": "https://github.com/tinyfists/gh-nonsense-internal"
              },
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgd1mYU",
                "name": "Repository",
                "dataType": "REPOSITORY"
              }
            },
            {
              "__typename": "ProjectV2ItemFieldTextValue",
              "text": "This is a title",
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgd1mX8",
                "name": "Title",
                "dataType": "TITLE"
              }
            },
            {
              "__typename": "ProjectV2ItemFieldNumberValue",
              "number": 2147483646.9876542,
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgnpmgo",
                "name": "Floater",
                "dataType": "NUMBER"
              }
            }
          ]
        }
      }
    }
  }
}

* Request took 595.488792ms
Edited item "This is a title"
```

</p>
</details> 

<details>
<summary>
  <b><code>--number 2147483646.1234567890</code> converts to <code>2147483646.1234567</code></b>
</summary>
<p>

```shell
andyfeller@Andys-MBP:cli/cli ‹10342-gh-project-item-edit-number›$ GH_DEBUG=api ./bin/gh project item-edit --project-id PVT_kwDOBWmBn84Al2QA --id PVTI_lADOBWmBn84Al2QAzgXCZ4c --field-id PVTF_lADOBWmBn84Al2QAzgnpmgo --number 2147483646.1234567890
* Request at 2025-02-05 09:25:10.33057 -0500 EST m=+0.064031876
* Request to https://api.github.com/graphql
> POST /graphql HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Length: 3204
> Content-Type: application/json
> Graphql-Features: merge_queue
> Time-Zone: America/New_York
> User-Agent: GitHub CLI v2.66.1-72-g64b63f61

GraphQL query:
mutation UpdateItemValues($input:UpdateProjectV2ItemFieldValueInput!){updateProjectV2ItemFieldValue(input:$input){projectV2Item{content{__typename,... on DraftIssue{id,body,title},... on PullRequest{body,title,number,url,repository{nameWithOwner}},... on Issue{body,title,number,url,repository{nameWithOwner}}},id,fieldValues(first: 100){nodes{__typename,... on ProjectV2ItemFieldDateValue{date,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldIterationValue{title,startDate,duration,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}},iterationId},... on ProjectV2ItemFieldLabelValue{labels(first: 10){nodes{name}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldNumberValue{number,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldSingleSelectValue{name,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldTextValue{text,field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldMilestoneValue{milestone{title,description,dueOn},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldPullRequestValue{pullRequests(first:10){nodes{url}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldRepositoryValue{repository{url},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldUserValue{users(first: 10){nodes{login}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}},... on ProjectV2ItemFieldReviewerValue{reviewers(first: 10){nodes{__typename,... on Team{name},... on User{login}}},field{__typename,... on ProjectV2Field{id,name,dataType},... on ProjectV2IterationField{id,name,dataType},... on ProjectV2SingleSelectField{id,name,dataType,options{id,name}}}}}}}}}
GraphQL variables: {"input":{"projectId":"PVT_kwDOBWmBn84Al2QA","itemId":"PVTI_lADOBWmBn84Al2QAzgXCZ4c","fieldId":"PVTF_lADOBWmBn84Al2QAzgnpmgo","value":{"number":2147483646.1234567}}}

< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Wed, 05 Feb 2025 14:25:10 GMT
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: repo
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: D060:25126:A8AD1E:1527D9E:67A374C6
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: admin:org, admin:public_key, admin:ssh_signing_key, gist, project, repo, workflow
< X-Ratelimit-Limit: 5000
< X-Ratelimit-Remaining: 5000
< X-Ratelimit-Reset: 1738765759
< X-Ratelimit-Resource: graphql
< X-Ratelimit-Used: 714
< X-Xss-Protection: 0

{
  "data": {
    "updateProjectV2ItemFieldValue": {
      "projectV2Item": {
        "content": {
          "__typename": "Issue",
          "body": "This is the body",
          "title": "This is a title",
          "number": 37,
          "url": "https://github.com/tinyfists/gh-nonsense-internal/issues/37",
          "repository": {
            "nameWithOwner": "tinyfists/gh-nonsense-internal"
          }
        },
        "id": "PVTI_lADOBWmBn84Al2QAzgXCZ4c",
        "fieldValues": {
          "nodes": [
            {
              "__typename": "ProjectV2ItemFieldRepositoryValue",
              "repository": {
                "url": "https://github.com/tinyfists/gh-nonsense-internal"
              },
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgd1mYU",
                "name": "Repository",
                "dataType": "REPOSITORY"
              }
            },
            {
              "__typename": "ProjectV2ItemFieldTextValue",
              "text": "This is a title",
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgd1mX8",
                "name": "Title",
                "dataType": "TITLE"
              }
            },
            {
              "__typename": "ProjectV2ItemFieldNumberValue",
              "number": 2147483646.1234567,
              "field": {
                "__typename": "ProjectV2Field",
                "id": "PVTF_lADOBWmBn84Al2QAzgnpmgo",
                "name": "Floater",
                "dataType": "NUMBER"
              }
            }
          ]
        }
      }
    }
  }
}

* Request took 613.7205ms
Edited item "This is a title"
```

</p>
</details> 
## Acceptance Criteria

@iamazeem @mikekistler : okay, wrapped up digging into this.  I'd like to propose the following AC for to wrap up this issue.  I don't think a whole lot of work needs to be done for most of these, but just stating clearly what the expectations once the work is merged.

1. **Given** v2 project with a `Number` field
   **When** `gh project item-edit --number VALUE` is called where `VALUE <= 2147483647` and `VALUE >= -2147483647`
   **Then** `VALUE` is converted to a 64-bit float according to [Go `strconv.ParseFloat(..., 64)`](https://pkg.go.dev/strconv#ParseFloat) and the item field is updated.

1. **Given** v2 project with a `Number` field
   **And Given** project item where this field has been set
   **When** `gh project item-list --format json` is called
   **Then** `Number` field is return to the user without any loss of precision

### Update

After working with @BagToad, we're removing the client-side validation AC from this as AC as Projects team is going to improve the server-side validation messaging so we don't need to duplicate it.
@iamazeem : I've assigned the issue to you given your interest; thank you again for your continued support! ❤ 
@andyfeller: Thank you for the detailed investigation and explanation! 🏆

Here's another case that I found out for `--number` that 0 is discarded as an invalid value:

```shell
$ bin/gh project item-edit --project-id $projectId --id $itemId --field-id $fieldId --number 0
error: no changes to make
```

However, given above valid ranges, it is a valid value and can be set from UI:

![Image](https://github.com/user-attachments/assets/9b1591dc-82f8-4a5d-a3e5-2fb5f685a111)

Please update [AC](https://github.com/cli/cli/issues/10342#issuecomment-2637973446) accordingly. Thanks!
@iamazeem : have you looked in the history behind that behavior?

### Let's dig into it

There are a few places in code that checks for `--number 0`, which harken back to https://github.com/cli/cli/pull/7375 where `gh project` commandset was introduced:

https://github.com/cli/cli/blob/756ba75c9ded6d1d201ddef4f271f4ba37057659/pkg/cmd/project/item-edit/item_edit.go#L79-L96

https://github.com/cli/cli/blob/756ba75c9ded6d1d201ddef4f271f4ba37057659/pkg/cmd/project/item-edit/item_edit.go#L148-L155

I suspect the challenge we have here is that `0` is the zero value for 32-bit and 64-bit floats even if the user does not provide `--number` flag.

To be clear, this is a problem, however let's proceed with the issue AC we have so far and file a separate issue for this new discovery while I confirm this with original hubber who introduced the commandset and figure out potential workarounds. 🤔 
created https://github.com/cli/cli/issues/10380 for `--number 0` and will add notes on potentially how to address it



Patch:
diff --git a/pkg/cmd/project/item-edit/item_edit.go b/pkg/cmd/project/item-edit/item_edit.go
index 63dcb3b39e9..657af1f54ac 100644
--- a/pkg/cmd/project/item-edit/item_edit.go
+++ b/pkg/cmd/project/item-edit/item_edit.go
@@ -23,7 +23,7 @@ type editItemOpts struct {
 	fieldID              string
 	projectID            string
 	text                 string
-	number               float32
+	number               float64
 	date                 string
 	singleSelectOptionID string
 	iterationID          string
@@ -123,7 +123,7 @@ func NewCmdEditItem(f *cmdutil.Factory, runF func(config editItemConfig) error)
 	editItemCmd.Flags().StringVar(&opts.fieldID, "field-id", "", "ID of the field to update")
 	editItemCmd.Flags().StringVar(&opts.projectID, "project-id", "", "ID of the project to which the field belongs to")
 	editItemCmd.Flags().StringVar(&opts.text, "text", "", "Text value for the field")
-	editItemCmd.Flags().Float32Var(&opts.number, "number", 0, "Number value for the field")
+	editItemCmd.Flags().Float64Var(&opts.number, "number", 0, "Number value for the field")
 	editItemCmd.Flags().StringVar(&opts.date, "date", "", "Date value for the field (YYYY-MM-DD)")
 	editItemCmd.Flags().StringVar(&opts.singleSelectOptionID, "single-select-option-id", "", "ID of the single select option value to set on the field")
 	editItemCmd.Flags().StringVar(&opts.iterationID, "iteration-id", "", "ID of the iteration value to set on the field")
diff --git a/pkg/cmd/project/shared/queries/queries.go b/pkg/cmd/project/shared/queries/queries.go
index 5855e906ecc..3e63465dd08 100644
--- a/pkg/cmd/project/shared/queries/queries.go
+++ b/pkg/cmd/project/shared/queries/queries.go
@@ -250,7 +250,7 @@ type FieldValueNodes struct {
 		Field ProjectField
 	} `graphql:"... on ProjectV2ItemFieldLabelValue"`
 	ProjectV2ItemFieldNumberValue struct {
-		Number float32
+		Number float64
 		Field  ProjectField
 	} `graphql:"... on ProjectV2ItemFieldNumberValue"`
 	ProjectV2ItemFieldSingleSelectValue struct {


Test patch:
diff --git a/pkg/cmd/project/item-edit/item_edit_test.go b/pkg/cmd/project/item-edit/item_edit_test.go
index fb9097d19f5..2f9a16df691 100644
--- a/pkg/cmd/project/item-edit/item_edit_test.go
+++ b/pkg/cmd/project/item-edit/item_edit_test.go
@@ -47,6 +47,14 @@ func TestNewCmdeditItem(t *testing.T) {
 				itemID: "123",
 			},
 		},
+		{
+			name: "number with floating point value",
+			cli:  "--number 123.45 --id 123",
+			wants: editItemOpts{
+				number: 123.45,
+				itemID: "123",
+			},
+		},
 		{
 			name: "field-id",
 			cli:  "--field-id FIELD_ID --id 123",
@@ -255,7 +263,7 @@ func TestRunItemEdit_Number(t *testing.T) {
 	// edit item
 	gock.New("https://api.github.com").
 		Post("/graphql").
-		BodyString(`{"query":"mutation UpdateItemValues.*","variables":{"input":{"projectId":"project_id","itemId":"item_id","fieldId":"field_id","value":{"number":2}}}}`).
+		BodyString(`{"query":"mutation UpdateItemValues.*","variables":{"input":{"projectId":"project_id","itemId":"item_id","fieldId":"field_id","value":{"number":123.45}}}}`).
 		Reply(200).
 		JSON(map[string]interface{}{
 			"data": map[string]interface{}{
@@ -284,7 +292,7 @@ func TestRunItemEdit_Number(t *testing.T) {
 	config := editItemConfig{
 		io: ios,
 		opts: editItemOpts{
-			number:    2,
+			number:    123.45,
 			itemID:    "item_id",
 			projectID: "project_id",
 			fieldID:   "field_id",