Skip to content

Add Statsig sunset gate bypass and cloud control logger#26

Open
Meow-Emperor wants to merge 1 commit intoHaleclipse:masterfrom
Meow-Emperor:master
Open

Add Statsig sunset gate bypass and cloud control logger#26
Meow-Emperor wants to merge 1 commit intoHaleclipse:masterfrom
Meow-Emperor:master

Conversation

@Meow-Emperor
Copy link

@Meow-Emperor Meow-Emperor commented Mar 6, 2026

  • patch-sunset.js: disable app sunset forced update (gate 2929582856)
  • patch-statsig-logger.js: inject real-time Statsig values logging
  • STATSIG_GATES.md: document all 30 gates, 15 configs, 6 layers
  • Update patch chain: copyright + i18n + sunset

Summary by Sourcery

Introduce post-build patches to bypass the Statsig-controlled app sunset gate, add real-time Statsig value logging, and document the current Statsig gates/configs/layers, while updating dev startup and patch scripts to support the new behavior.

New Features:

  • Add a post-build patch to disable the Statsig sunset feature gate so the app continues to function regardless of forced update settings.
  • Add a post-build patch that injects logging into the Statsig client to print all feature gates, dynamic configs, and layers when values update.
  • Document the discovered Statsig feature gates, dynamic configs, and layers in a dedicated mapping file.

Enhancements:

  • Sync the Codex CLI binary from the npm vendor directory into resources/bin at dev startup to simplify local development setup.
  • Extend the patch npm script to run multiple patch steps (copyright, i18n, sunset, devtools) before packaging.

- patch-sunset.js: disable app sunset forced update (gate 2929582856)
- patch-statsig-logger.js: inject real-time Statsig values logging
- STATSIG_GATES.md: document all 30 gates, 15 configs, 6 layers
- Update patch chain: copyright + i18n + sunset
@sourcery-ai
Copy link

sourcery-ai bot commented Mar 6, 2026

Reviewer's Guide

Adds build-time AST patch scripts to permanently bypass the Statsig app sunset gate and to inject verbose Statsig cloud-control logging into the webview bundle, wires these patches into the build/patch pipeline, documents the discovered Statsig gates/configs/layers, and improves dev CLI binary resolution by syncing from the npm vendor directory.

Sequence diagram for Statsig sunset gate evaluation with bypass

sequenceDiagram
  actor User
  participant App
  participant Webview
  participant StatsigClient
  participant SunsetGuard

  User->>App: launch Codex
  App->>Webview: load webview bundle
  Webview->>StatsigClient: initialize Statsig
  StatsigClient-->>Webview: feature gate values loaded

  Webview->>SunsetGuard: render sunset wrapper
  SunsetGuard->>StatsigClient: get gate 2929582856 value
  Note over SunsetGuard,StatsigClient: Before patch: Cs(i) returns server controlled boolean

  StatsigClient-->>SunsetGuard: gate expression evaluated as constant false
  Note over SunsetGuard: After patch-sunset
  Note over SunsetGuard: Cs(i) replaced by !1

  SunsetGuard-->>Webview: allow children render
  Webview-->>User: normal UI visible
Loading

Sequence diagram for injected Statsig cloud control logger

sequenceDiagram
  participant Webview
  participant StatsigClient
  participant ValuesStore
  participant Logger

  Webview->>StatsigClient: initialize
  StatsigClient->>ValuesStore: fetch gates configs layers
  ValuesStore-->>StatsigClient: values loaded

  StatsigClient->>StatsigClient: _setStatus(g Ready)
  activate StatsigClient
  StatsigClient->>Logger: injected logger block executes
  Logger->>ValuesStore: read feature_gates dynamic_configs layer_configs param_stores values
  Logger-->>Logger: group and print gate counts
  Logger-->>Logger: log each gate and rule
  Logger-->>Logger: log dynamic configs and layers
  Logger-->>Logger: log param stores and raw keys
  deactivate StatsigClient

  StatsigClient-->>Webview: emit values_updated status
Loading

File-Level Changes

Change Details Files
Improve dev CLI binary resolution by syncing the codex binary from the npm @cometix/codex vendor directory into resources/bin before startup.
  • Introduce a TARGET_TRIPLE_MAP from platform-specific binDir values to Rust-style target triples to locate the correct vendor binary.
  • On start, if a matching triple exists and the vendor binary is present, ensure resources/bin/ exists, copy the vendor codex binary into it, and chmod it executable on POSIX.
  • Adjust the CLI existence error message to mention both resources/bin and node_modules/@cometix/codex/vendor as search locations.
scripts/start-dev.js
Extend the patch pipeline so multiple post-build patches (copyright, i18n, sunset gate, devtools) are applied before packaging.
  • Change the npm patch script to run patch-copyright.js, patch-i18n.js, patch-sunset.js, and patch-devtools.js in sequence.
  • Keep the forge commands wired through the updated patch script so all patches are applied before electron-forge make.
package.json
Add an AST-based patch script to inject a Statsig values logger into the webview bundle, logging all feature gates, dynamic configs, layers, param stores, and raw keys when Statsig becomes Ready.
  • Use acorn to parse the webview index-*.js bundle under src/webview/assets and a custom recursive walker to traverse the AST.
  • Define a rule that locates the _setStatus method (as a Property or MethodDefinition) whose body contains the string "values_updated" and has not yet been patched, then inject a try/catch console logging block at the start of the function body.
  • The injected logger reads from this._store._values, derives feature_gates, dynamic_configs, layer_configs, param_stores, and values, and logs them via console.group/console.log along with the store source; supports a --check mode that only reports matches without modifying the file.
  • Ensure idempotence by skipping injection if the marker string "[Statsig] values_updated" already exists in the method body and by deduplicating patch positions.
scripts/patch-statsig-logger.js
Add an AST-based patch script to disable the Statsig app sunset gate (ID 2929582856) by replacing its useGateValue calls with a constant false expression.
  • Use acorn and the shared walk helper to parse and traverse the same webview index-*.js bundle.
  • Define a rule that finds functions whose source contains the literal "2929582856" (the sunset gate ID) and, within those functions, locates CallExpression nodes of the form Cs(Identifier).
  • For each matching call, generate a patch that replaces the original callee expression source with the literal !1 (false), while making the patch idempotent by skipping calls already equal to !1 and deduplicating by start offset.
  • Provide a --check mode that prints a report of all replacements (original → replacement) without writing, and on normal runs apply all patches in reverse source order and report how many gate calls were disabled.
scripts/patch-sunset.js
Document the discovered Statsig gates, dynamic configs, layers, and their inferred client usage, plus an overview of the patch scripts that touch Statsig and related behavior.
  • Create a STATSIG_GATES.md reference that lists 30 feature gates (including 2929582856 for app sunset), 15 dynamic configs, and 6 layers, with IDs, inferred feature names, and bound components/functions where possible.
  • Explain that IDs are DJB2 hashes of server-side names, that clients do not store the original names, and that some gates/configs/layers are server-only or conditionally delivered.
  • Summarize each patch script (patch-sunset.js, patch-statsig-logger.js, patch-i18n.js, patch-devtools.js, patch-copyright.js, patch-process-polyfill.js) and their targets/strategies, plus notes on default gate values and sunset behavior.
scripts/STATSIG_GATES.md
Remove the process polyfill patch script from the codebase.
  • Delete patch-process-polyfill.js from the scripts directory, implying it is no longer needed in the patch chain.
scripts/patch-process-polyfill.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The sunset patch currently replaces every Cs(...) call inside any function whose source happens to contain "2929582856"; consider tightening the matcher to bind the specific Literal node for that ID to the CallExpression argument so that only the intended gate invocation is rewritten and unrelated Cs calls in the same function are not affected.
  • Both patch-sunset.js and patch-statsig-logger.js duplicate logic for AST walking and bundle location; extracting these into a shared utility (e.g., scripts/patch-utils.js) would reduce duplication and make future patch scripts easier to maintain.
  • In start-dev.js, the vendor → resources/bin sync runs on every invocation; you might want to short-circuit when the target binary already exists and is up to date to avoid unnecessary filesystem work on each dev start.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The sunset patch currently replaces every `Cs(...)` call inside any function whose source happens to contain `"2929582856"`; consider tightening the matcher to bind the specific `Literal` node for that ID to the `CallExpression` argument so that only the intended gate invocation is rewritten and unrelated `Cs` calls in the same function are not affected.
- Both `patch-sunset.js` and `patch-statsig-logger.js` duplicate logic for AST walking and bundle location; extracting these into a shared utility (e.g., `scripts/patch-utils.js`) would reduce duplication and make future patch scripts easier to maintain.
- In `start-dev.js`, the vendor → `resources/bin` sync runs on every invocation; you might want to short-circuit when the target binary already exists and is up to date to avoid unnecessary filesystem work on each dev start.

## Individual Comments

### Comment 1
<location path="scripts/start-dev.js" line_range="54-56" />
<code_context>
+    const localDir = path.join(__dirname, '..', 'resources', 'bin', binDir);
+    fs.mkdirSync(localDir, { recursive: true });
+    fs.copyFileSync(vendorPath, path.join(localDir, cliName));
+    try { fs.chmodSync(path.join(localDir, cliName), 0o755); } catch {}
+    console.log(`[start-dev] Synced codex binary: vendor → resources/bin/${binDir}/${cliName}`);
+  }
</code_context>
<issue_to_address>
**suggestion:** Swallowing chmodSync errors completely may hide real filesystem issues.

Since any `chmodSync` failure is ignored, permission problems in restrictive environments or with unusual umasks may go unnoticed. Consider at least emitting a brief warning (optionally behind an env flag) so unexpected failures are visible without interrupting execution.

```suggestion
    const localDir = path.join(__dirname, '..', 'resources', 'bin', binDir);
    fs.mkdirSync(localDir, { recursive: true });
    const localCliPath = path.join(localDir, cliName);
    fs.copyFileSync(vendorPath, localCliPath);
    try {
      fs.chmodSync(localCliPath, 0o755);
    } catch (err) {
      if (!process.env.START_DEV_SILENT_CHMOD_WARN) {
        console.warn(`[start-dev] Warning: failed to chmod codex binary at ${localCliPath}: ${err && err.message ? err.message : err}`);
      }
    }
    console.log(`[start-dev] Synced codex binary: vendor → resources/bin/${binDir}/${cliName}`);
```
</issue_to_address>

### Comment 2
<location path="scripts/patch-sunset.js" line_range="49-58" />
<code_context>
+//  Patch 规则
+// ──────────────────────────────────────────────
+
+const SUNSET_GATE_ID = "2929582856";
+
+const RULES = [
+  {
+    id: "disable_sunset_gate",
+    description: `Cs(var) → !1  (gate "${SUNSET_GATE_ID}" 对应的 useGateValue 调用)`,
+    /**
+     * AST 匹配策略:
+     *   在包含字符串常量 "2929582856" 的函数中,
+     *   找到 Cs(identifier) 形式的 CallExpression,
+     *   将整个调用替换为 !1
+     *
+     * 匹配条件:
+     *   1. 函数体中存在 Literal "2929582856"
+     *   2. 该函数体中存在 CallExpression: Cs(Identifier)
+     *   3. Cs 是 Identifier(不是 MemberExpression)
+     */
+    match(node, source) {
+      // 寻找包含 sunset gate ID 的函数体
+      if (node.type !== "FunctionDeclaration" && node.type !== "FunctionExpression" &&
+          node.type !== "ArrowFunctionExpression") return null;
+
+      const funcSrc = source.slice(node.start, node.end);
+
+      // 快速预筛:函数体中必须包含 gate ID
</code_context>
<issue_to_address>
**issue (bug_risk):** Matching all `Cs(...)` calls in any function containing the gate ID risks over-disabling gates.

Because the rule rewrites every `Cs(Identifier)` within any function whose source contains `"2929582856"`, it will also flip other gates (or unrelated `Cs` calls) if they share the same function. To avoid over-disabling, first resolve which identifier is bound to `"2929582856"` and only rewrite `Cs(<thatVar>)`, or add tighter textual/AST constraints around the specific call site to narrow the match.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +54 to +56
const localDir = path.join(__dirname, '..', 'resources', 'bin', binDir);
fs.mkdirSync(localDir, { recursive: true });
fs.copyFileSync(vendorPath, path.join(localDir, cliName));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Swallowing chmodSync errors completely may hide real filesystem issues.

Since any chmodSync failure is ignored, permission problems in restrictive environments or with unusual umasks may go unnoticed. Consider at least emitting a brief warning (optionally behind an env flag) so unexpected failures are visible without interrupting execution.

Suggested change
const localDir = path.join(__dirname, '..', 'resources', 'bin', binDir);
fs.mkdirSync(localDir, { recursive: true });
fs.copyFileSync(vendorPath, path.join(localDir, cliName));
const localDir = path.join(__dirname, '..', 'resources', 'bin', binDir);
fs.mkdirSync(localDir, { recursive: true });
const localCliPath = path.join(localDir, cliName);
fs.copyFileSync(vendorPath, localCliPath);
try {
fs.chmodSync(localCliPath, 0o755);
} catch (err) {
if (!process.env.START_DEV_SILENT_CHMOD_WARN) {
console.warn(`[start-dev] Warning: failed to chmod codex binary at ${localCliPath}: ${err && err.message ? err.message : err}`);
}
}
console.log(`[start-dev] Synced codex binary: vendor → resources/bin/${binDir}/${cliName}`);

Comment on lines +49 to +58
const SUNSET_GATE_ID = "2929582856";

const RULES = [
{
id: "disable_sunset_gate",
description: `Cs(var) → !1 (gate "${SUNSET_GATE_ID}" 对应的 useGateValue 调用)`,
/**
* AST 匹配策略:
* 在包含字符串常量 "2929582856" 的函数中,
* 找到 Cs(identifier) 形式的 CallExpression,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Matching all Cs(...) calls in any function containing the gate ID risks over-disabling gates.

Because the rule rewrites every Cs(Identifier) within any function whose source contains "2929582856", it will also flip other gates (or unrelated Cs calls) if they share the same function. To avoid over-disabling, first resolve which identifier is bound to "2929582856" and only rewrite Cs(<thatVar>), or add tighter textual/AST constraints around the specific call site to narrow the match.

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.

2 participants