Add Statsig sunset gate bypass and cloud control logger#26
Add Statsig sunset gate bypass and cloud control logger#26Meow-Emperor wants to merge 1 commit intoHaleclipse:masterfrom
Conversation
- 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
Reviewer's GuideAdds 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 bypasssequenceDiagram
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
Sequence diagram for injected Statsig cloud control loggersequenceDiagram
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
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
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 specificLiteralnode for that ID to theCallExpressionargument so that only the intended gate invocation is rewritten and unrelatedCscalls in the same function are not affected. - Both
patch-sunset.jsandpatch-statsig-logger.jsduplicate 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/binsync 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| const localDir = path.join(__dirname, '..', 'resources', 'bin', binDir); | ||
| fs.mkdirSync(localDir, { recursive: true }); | ||
| fs.copyFileSync(vendorPath, path.join(localDir, cliName)); |
There was a problem hiding this comment.
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.
| 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}`); |
| 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, |
There was a problem hiding this comment.
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.
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:
Enhancements: