Skip to content

fix(set): take Go's --sync=false spelling instead of dying at the parser - #399

Merged
GeiserX merged 1 commit into
mainfrom
factory/tsd-aud310-c583-r1-s1n0/set-go-bool-spellings
Sep 7, 2026
Merged

fix(set): take Go's --sync=false spelling instead of dying at the parser#399
GeiserX merged 1 commit into
mainfrom
factory/tsd-aud310-c583-r1-s1n0/set-go-bool-spellings

Conversation

@GeiserX

@GeiserX GeiserX commented Sep 7, 2026

Copy link
Copy Markdown
Owner

tnet set carries Go's --remote-config and --sync so that a command
line ported from Go reaches a refusal that names what this build cannot
do, instead of clap's "unexpected argument". That only worked for the
flag NAME. Both were declared as valueless clap flags, so Go's own way of
turning them off went nowhere:

$ tnet set --sync=false
error: unexpected value 'false' for '--sync' found; no more were expected
exit 2

$ tnet set --remote-config=false
error: unexpected value 'false' for '--remote-config' found; no more were expected
exit 2

Upstream registers both with flag.BoolVar (cmd/tailscale/cli/set.go
@ 53a0d659afa51835dd7a9283873cca44261454f8, newSetFlagSet), so
--sync=false and --remote-config=false are exactly how a Go command
line spells "off" — and --sync=false is the one value of that flag this
build has anything to say about. A ported line hit the same wall the
flags were added to remove.

WHAT CHANGED

--remote-config and --sync now take Go's optional attached value:
num_args = 0..=1 + require_equals + default_missing_value = "true".
That is Go's bool-flag grammar precisely — bare --sync is the flag's
presence, and a value can only arrive as --sync=<v> because Go's
IsBoolFlag never lets a bool flag consume the following argument. The
value spellings are strconv.ParseBool's, via a new parse_go_bool, so
--sync=0 and --sync=T work as they do in Go and a value Go rejects is
a parse error naming the flag rather than a silent "off":

$ tnet set --sync=nope
error: invalid value 'nope' for '--sync[=<BOOL>]': strconv.ParseBool: parsing "nope": invalid syntax

--no-sync / --no-remote-config stay, unchanged: they are this fork's
existing spelling, the README names them, and the refusal messages quote
them. A new resolve_go_bool_tristate folds the two spellings of one
switch into the single Option<bool> the gate already reads, so both
land on the same sentence. clap still refuses the pair together, in
either order. After the change:

$ tnet set --sync=false
Error: --no-sync (Go `--sync=false`) is not supported by this build: ... engine ask #34 ...
exit 1

$ tnet set --remote-config=false     # asks for the status quo — accepted, goes to the daemon

No behaviour moved for any command line that already parsed, and no
refusal text changed.

WHAT TO READ FIRST

src/bin/tnet.rs: the two #[arg(...)] blocks on Command::Set, then
parse_go_bool and resolve_go_bool_tristate next to
check_unmodelled_set_flags. The test is
gos_own_sync_and_remote_config_spellings_reach_the_ported_refusal; it
calls the real clap parser and the real gate, and pins that Go's off
spelling produces the byte-identical refusal --no-sync produces. It
cannot pass against the previous declarations — --sync=false did not
parse at all.

VERIFICATION

cargo fmt --all --check                  clean
cargo clippy --all-targets -- -D warnings clean
cargo test --all-targets                 all suites ok, 0 failed
cargo build --release --bins             ok

The before/after transcripts above are from ./target/debug/tnet, built
at the parent commit and then with this change.

STALE ELSEWHERE

docs/PARITY_GAP_ANALYSIS.md (the #34 row) and the README's "Four of
Go's set flags are parsed but not modelled" paragraph both describe the
refusals only in this fork's --no- spelling. Both are still true, but
they now understate the port: Go's own --sync=false /
--remote-config=false reach those same refusals. Left unedited on
purpose, to keep the ledger free of collisions.

DECISIONS

  • Kept the refusal wording as it is, including its --no-sync (Go --sync=false) opening. It already names both spellings, and a
    reworded message would churn text the README and the existing tests
    are written against for no gain in what it tells the operator.
  • Used require_equals rather than letting --sync false bind. Go's
    flag package never passes the following argument to a bool flag, so
    binding it would accept a line Go reads differently.
  • parse_go_bool is a second, local copy: tailnetd has the same
    function for cmd/tailscaled's boolFlag, but the two binaries share
    no flag plumbing and hoisting it into the library would touch a binary
    this change has no business in.

NOTED, NOT FIXED

  • The other set boolean pairs (--accept-routes/--no-accept-routes,
    --ssh, --webclient, …) are flag.BoolVar upstream too, so
    --accept-routes=false still dies at clap the same way. Same class of
    divergence, wider blast radius, and none of those has a by-name
    refusal riding on it — worth its own change.
  • tnet set has no catch-all for leftover non-flag arguments, so
    tnet set foo is clap's "unexpected argument" (exit 2) rather than
    Go's own message from runSet (exit 1). Also separate.

bead tsd-aud310 · session tsd-aud310-c583-r1-s1n0 · base refs/heads/main

…arser

`tnet set` carries Go's `--remote-config` and `--sync` so that a command
line ported from Go reaches a refusal that names what this build cannot
do, instead of clap's "unexpected argument". That only worked for the
flag NAME. Both were declared as valueless clap flags, so Go's own way of
turning them off went nowhere:

    $ tnet set --sync=false
    error: unexpected value 'false' for '--sync' found; no more were expected
    exit 2

    $ tnet set --remote-config=false
    error: unexpected value 'false' for '--remote-config' found; no more were expected
    exit 2

Upstream registers both with `flag.BoolVar` (`cmd/tailscale/cli/set.go`
@ 53a0d659afa51835dd7a9283873cca44261454f8, `newSetFlagSet`), so
`--sync=false` and `--remote-config=false` are exactly how a Go command
line spells "off" — and `--sync=false` is the one value of that flag this
build has anything to say about. A ported line hit the same wall the
flags were added to remove.

WHAT CHANGED

`--remote-config` and `--sync` now take Go's optional attached value:
`num_args = 0..=1` + `require_equals` + `default_missing_value = "true"`.
That is Go's bool-flag grammar precisely — bare `--sync` is the flag's
presence, and a value can only arrive as `--sync=<v>` because Go's
`IsBoolFlag` never lets a bool flag consume the following argument. The
value spellings are `strconv.ParseBool`'s, via a new `parse_go_bool`, so
`--sync=0` and `--sync=T` work as they do in Go and a value Go rejects is
a parse error naming the flag rather than a silent "off":

    $ tnet set --sync=nope
    error: invalid value 'nope' for '--sync[=<BOOL>]': strconv.ParseBool: parsing "nope": invalid syntax

`--no-sync` / `--no-remote-config` stay, unchanged: they are this fork's
existing spelling, the README names them, and the refusal messages quote
them. A new `resolve_go_bool_tristate` folds the two spellings of one
switch into the single `Option<bool>` the gate already reads, so both
land on the same sentence. clap still refuses the pair together, in
either order. After the change:

    $ tnet set --sync=false
    Error: --no-sync (Go `--sync=false`) is not supported by this build: ... engine ask #34 ...
    exit 1

    $ tnet set --remote-config=false     # asks for the status quo — accepted, goes to the daemon

No behaviour moved for any command line that already parsed, and no
refusal text changed.

WHAT TO READ FIRST

`src/bin/tnet.rs`: the two `#[arg(...)]` blocks on `Command::Set`, then
`parse_go_bool` and `resolve_go_bool_tristate` next to
`check_unmodelled_set_flags`. The test is
`gos_own_sync_and_remote_config_spellings_reach_the_ported_refusal`; it
calls the real clap parser and the real gate, and pins that Go's off
spelling produces the byte-identical refusal `--no-sync` produces. It
cannot pass against the previous declarations — `--sync=false` did not
parse at all.

VERIFICATION

    cargo fmt --all --check                  clean
    cargo clippy --all-targets -- -D warnings clean
    cargo test --all-targets                 all suites ok, 0 failed
    cargo build --release --bins             ok

The before/after transcripts above are from `./target/debug/tnet`, built
at the parent commit and then with this change.

STALE ELSEWHERE

`docs/PARITY_GAP_ANALYSIS.md` (the `#34` row) and the README's "Four of
Go's `set` flags are parsed but not modelled" paragraph both describe the
refusals only in this fork's `--no-` spelling. Both are still true, but
they now understate the port: Go's own `--sync=false` /
`--remote-config=false` reach those same refusals. Left unedited on
purpose, to keep the ledger free of collisions.

DECISIONS

- Kept the refusal wording as it is, including its `--no-sync (Go
  `--sync=false`)` opening. It already names both spellings, and a
  reworded message would churn text the README and the existing tests
  are written against for no gain in what it tells the operator.
- Used `require_equals` rather than letting `--sync false` bind. Go's
  flag package never passes the following argument to a bool flag, so
  binding it would accept a line Go reads differently.
- `parse_go_bool` is a second, local copy: `tailnetd` has the same
  function for `cmd/tailscaled`'s `boolFlag`, but the two binaries share
  no flag plumbing and hoisting it into the library would touch a binary
  this change has no business in.

NOTED, NOT FIXED

- The other `set` boolean pairs (`--accept-routes`/`--no-accept-routes`,
  `--ssh`, `--webclient`, …) are `flag.BoolVar` upstream too, so
  `--accept-routes=false` still dies at clap the same way. Same class of
  divergence, wider blast radius, and none of those has a by-name
  refusal riding on it — worth its own change.
- `tnet set` has no catch-all for leftover non-flag arguments, so
  `tnet set foo` is clap's "unexpected argument" (exit 2) rather than
  Go's own message from `runSet` (exit 1). Also separate.
@GeiserX GeiserX added factory graph-opened The factory was seen to open this pull request. labels Sep 7, 2026
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 16 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: a59d99cd-1618-4559-8818-55d65da0150d

📥 Commits

Reviewing files that changed from the base of the PR and between 9ac7ec8 and 57ae7b0.

📒 Files selected for processing (1)
  • src/bin/tnet.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@GeiserX GeiserX added the graph-green The judge was seen to return this pull request's first green verdict (every expected check passing). label Sep 7, 2026
@GeiserX
GeiserX merged commit 4c10557 into main Sep 7, 2026
6 checks passed
@GeiserX
GeiserX deleted the factory/tsd-aud310-c583-r1-s1n0/set-go-bool-spellings branch September 7, 2026 13:55
@GeiserX GeiserX added graph-merged The settle lane was seen to squash-merge this pull request. graph-done FINAL: the bead was seen to close. Stamped on every pull request of that session. labels Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

factory graph-done FINAL: the bead was seen to close. Stamped on every pull request of that session. graph-green The judge was seen to return this pull request's first green verdict (every expected check passing). graph-merged The settle lane was seen to squash-merge this pull request. graph-opened The factory was seen to open this pull request.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant