Skip to content

fix(cli): edge and cosmetic parity sweep from the 2026-07-24 audit (CLI-1990) - #5978

Draft
Coly010 wants to merge 1 commit into
developfrom
columferry/cli-1990-edgecosmetic-parity-sweep-from-the-2026-07-24-audit
Draft

fix(cli): edge and cosmetic parity sweep from the 2026-07-24 audit (CLI-1990)#5978
Coly010 wants to merge 1 commit into
developfrom
columferry/cli-1990-edgecosmetic-parity-sweep-from-the-2026-07-24-audit

Conversation

@Coly010

@Coly010 Coly010 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Batch sweep of the small confirmed edge/cosmetic Go-parity divergences from the 2026-07-24 audit. Every item was re-verified against apps/cli-go at current develop before changing anything (audit line numbers were stale); several items turned out to be already fixed or in-flight in other PRs and are listed as such.

Fixes CLI-1990 — https://linear.app/supabase/issue/CLI-1990/edgecosmetic-parity-sweep-from-the-2026-07-24-audit-batchable-tail

Item-by-item

# Item Status Notes
1 functions delete line colour Fixed Aqua slug + ref, stdout-gated (delete.go:20)
2 functions deploy success ref Aqua, Bundling Function: bold, No Functions specified… bold Fixed deploy.go:70,35, bundle.go:30; stdout-gated where stdout-bound
3 Prune bullets • <bold> Already fixed by #5947 (CLI-1974), commit c4b45874
4 serve supabase start is not running. Aqua Deferred open PR #5976 modifies the same assertLocalDbRunning hunk in shared/functions/serve.ts; one-liner to do after it merges
5 encryption update-root-key Finished line + bogus comment Fixed Aqua'd; comment claimed a nonexistent "render Aqua as plain" convention
6 start rollback missing Stopping containers... Fixed stderr, matching Go's DockerRemoveAll writer on the start-failure path (start.go:77)
7 --debug Pruned … reports Fixed Pruned containers:/volumes:/network: (singular network) [a b c] on stderr; prune stdout now collected (also removes a latent unread-pipe hazard); LegacyDebugFlag threaded through stop/start/rollback
8 Per-retry Retrying after Ns: <image> banner Fixed 4s/8s per Go's 2<<(i+1) schedule (docker.go:314); the failed attempt's error line is played by the already-teed docker pull stderr
9 inspect db blocking backtick blocking_statement Fixed col 2 only; col 5 (blocked_statement) stays bare per Go's format string (blocking.go:56)
10 seed buckets mutex bracket [local linked] Fixed cobra keeps registration order for the group list and sorts only the "were all set" list; corrected the misleading comment in legacy-db-target-flags.ts (storage's [linked local] stays correct)
11 snippets download 4 UUID forms + lowercase URL Fixed faithful uuid.Parse port incl. the braced-form s[1:] quirk; canonical lowercase interpolated into the URL; Go's three error branches verbatim
12 storage cp --jobs negative rejection Fixed pflag's exact uint error (invalid argument "-1" for "-j, --jobs" flag: strconv.ParseUint: …), before mutex validation and without telemetry; the documented 0 → 1 clamp stays (Go's 0 deadlocks)
13 postgres-config value coercion + %+v floats Fixed exact ParseBool case set; int64-overflow → verbatim string; pretty table renders numbers with Go's float64 %g (10000001e+06) via hoisted legacyGoFormatFloat (also reused by db query)
14 init template file modes Fixed 0644/0755 pinned; the gitignore append-branch write is deliberately left unpinned — mode only applies at creation, and #5977 rewrites that exact line
15 login fallback dir 0700→0755 Fixed Go pins the dir 0755 (access_token.go:91); the token file stays 0600, so no secret exposure
16 test new 0644 Fixed + dir 0755, like Go's utils.WriteFile
17 inspect report 0755/0644 Fixed
18 bootstrap invalid-stored-token fast-fail Deferred not small: resolveLegacyAccessToken deliberately collapses invalid→None for many callers (sso, snippets, postgres-config, …); distinguishing invalid-vs-missing needs a shared-semantics change
19 migration new Created line on stdin-copy failure Fixed mirrors Go's deferred Println (new.go:24-28); also stdout-gates the Bold path (CLI-1546 class)
20 telemetry state-file recovery Fixed all-or-nothing decode like Go's decodeState; a corrupt file resets enabled to true and rotates identity, exactly like Go
21 unlink/services ref-read error Fixed (services) unlink already matched Go; services now warns failed to load project ref: … on a read error and continues unlinked (TOCTOU NotFound stays silent, like Go's ErrNotLinked branch)
22 domains CNAME dump byte codes Kept documented premise inaccurate: the non-reproducible part of Go's %+v is a runtime pointer address for ValidationErrors, not byte codes; TS's deterministic rendering is already documented in domains.format.ts
23 sso --domains= explicit-empty Deferred sso update already matches Go (len-check drops it); the add fix touches sso/add/add.handler.ts, in-flight in open PR #5974
24 db dump --file "" Fixed keys off len > 0 like Go: empty means stdout, no file open, no Dumped schema to … line
25 network-restrictions CIDR-before-ref ordering Deferred, kept documented direction is inverted vs the issue text: TS validates CIDR before ref, Go resolves ref first in PersistentPreRunE; aligning overlaps open PR #5975 (incl. its integration test file). The SIDE_EFFECTS.md note frames this as intended based on an incomplete Go reading — worth revisiting after #5975

Known residuals (deliberate, documented in code)

  • postgres-config digits in (2^53, 2^63) still lose precision on the way in (JSON.stringify cannot emit exact int64 tokens); Go sends exact integers there. Values beyond int64 now match Go (string fallback).
  • Colour TTY gating: stderr-bound colour gates on stderr's TTY (per legacy-colors.ts/CLI-1546 convention), whereas Go's lipgloss gates everything on stdout. Deliberate, pre-existing convention; only observable when exactly one of stdout/stderr is a TTY.
  • Bun's util.styleText currently ignores validateStream/NO_COLOR (verified on Bun 1.3.x), so under Bun piped output still carries ANSI for all legacy colour sites — a pre-existing runtime gap that predates this PR and deserves its own issue.
  • The services warning's error suffix is Effect's error text, not Go's *PathError bytes — the failed to load project ref: prefix is the parity-bearing part.
  • --jobs abc/3.5 still surface Effect CLI's parser error rather than pflag's; this PR scopes to negatives (the only case Flag.integer accepts that Go rejects).

Review notes

Four-perspective review (architect / engineer / security / DX) run pre-PR; all approve. Engineer fuzz-verified legacyGoFormatFloat (23k values) and legacyParseSnippetUuid (~8k inputs) byte-identical to Go/google-uuid. Security signed off on the 0700→0755 fallback-dir change (token file unchanged at 0600, matches Go exactly). Remaining findings were the documented residuals above.

…LI-1990)

Batch of small confirmed Go-parity divergences, each verified against
apps/cli-go before fixing:

Output/colour
- functions delete/deploy: Aqua slug/ref and Bold bundling slug +
  No-Functions dir, with stdout-gated colour where the line is
  stdout-bound (delete.go:20, deploy.go:35,70, bundle.go:30)
- encryption update-root-key: Aqua'd Finished line; removed the comment
  inventing a render-Aqua-as-plain convention (update.go:26)
- start rollback: print "Stopping containers..." to stderr
  (docker.go:97 via start.go:77)
- --debug prune reports: Pruned containers/volumes/network lines on
  stderr from legacyDockerRemoveAll, threading LegacyDebugFlag through
  stop/start (docker.go:123-143; "network" singular)
- per-retry image-pull banner: Retrying after 4s/8s: <image>
  (docker.go:312-314)
- inspect db blocking: backtick-wrap blocking_statement (col 2) like
  Go's row format (blocking.go:56)
- seed buckets: mutex group bracket keeps Go's registration order
  [local linked] (cmd/seed.go:32, cobra flag_groups.go:73)

Input handling
- snippets download: accept google/uuid Parse's 4 forms incl. the
  braced-form s[1:] quirk, canonical lowercase into the URL, Go's three
  error branches verbatim (download.go:15-19, uuid.go:68-117)
- storage cp: reject negative --jobs with pflag's uint error template;
  keep the documented 0->1 clamp (cmd/storage.go:107)
- postgres-config: strconv.ParseBool's exact case-sensitive set,
  int64-overflow falls back to the verbatim string, and the pretty
  table renders numbers with Go's %+v float64 formatting via the
  hoisted legacyGoFormatFloat (update.go:41-49, get.go:32-35)

Filesystem modes (Go pins them; Node defaulted)
- init templates 0644/0755, test new 0644/0755, inspect report
  0755/0644, login fallback dir 0700 -> Go's 0755 (token stays 0600)

Misc edges
- migration new: print the Created line even when the stdin copy fails
  (Go's deferred Println, new.go:24-28), stdout-gated Bold
- telemetry state: all-or-nothing decode like Go's decodeState -- a
  partially-corrupt file recreates everything fresh (state.go:87-115)
- services: warn failed to load project ref on a ref-file read error
  and continue unlinked (services.go:18-20)
- db dump: --file "" means stdout, no Dumped-schema line
  (dump.go:20-32, cmd/db.go:152-159)

Fixes CLI-1990
@Coly010

Coly010 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2fbd7309a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import { operationDefinitions, type ApiClient } from "@supabase/api/effect";
import { Effect, type Option } from "effect";
import * as HttpClientError from "effect/unstable/http/HttpClientError";
import { legacyAqua } from "../../legacy/shared/legacy-colors.ts";

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 Badge Keep legacy styling out of the shared delete path

When a v3/next user runs functions delete with a color-capable stdout, next/commands/functions/delete/delete.handler.ts calls this shared function and now receives legacy Go-specific ANSI styling; this import also makes the next command transitively depend on the legacy tree. Apply the coloring in the legacy handler or inject shell-specific formatting so the next shell remains isolated.

AGENTS.md reference: apps/cli/AGENTS.md:L18-L21

Useful? React with 👍 / 👎.

Comment on lines +78 to +80
if (Option.isSome(flags.jobs) && flags.jobs.value < 0) {
return yield* new LegacyStorageInvalidJobsError(flags.jobs.value);
}

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 Badge Validate unsigned jobs before entering the handler

For storage cp --jobs=-1 without --experimental, this check is never reached because legacyRequireExperimental fails first; positional parsing can similarly preempt it. The Go command declares jobs with UintVarP in cmd/storage.go, so the negative value fails during flag parsing before PersistentPreRunE and must report the ParseUint error regardless of those later conditions. Put the unsigned validation in the flag parser/refinement rather than inside the handler.

AGENTS.md reference: apps/cli/AGENTS.md:L246-L256

Useful? React with 👍 / 👎.

Comment on lines +90 to +92
if (typeof rawLastActive === "string") {
if (!RFC3339_RE.test(rawLastActive) || !Number.isFinite(Date.parse(rawLastActive))) {
return undefined;

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 Badge Reject calendar-invalid RFC3339 timestamps

When telemetry.json contains a timestamp such as 2025-02-29T00:00:00Z, the regex matches and JavaScript Date.parse normalizes it to March 1, while Go's time.Parse(time.RFC3339Nano, ...) rejects it. The file is therefore treated as valid and its enabled state and identities are preserved instead of triggering the all-or-nothing regeneration this change is intended to match; use a strict RFC3339 calendar validation rather than Date.parse validity alone.

AGENTS.md reference: apps/cli/AGENTS.md:L246-L256

Useful? React with 👍 / 👎.

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