Skip to content

Scaffold oxlint plugin with initial rule#2603

Merged
juliusmarminge merged 5 commits intomainfrom
t3code/23154b3f
May 8, 2026
Merged

Scaffold oxlint plugin with initial rule#2603
juliusmarminge merged 5 commits intomainfrom
t3code/23154b3f

Conversation

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge commented May 8, 2026

Note

Medium Risk
Broad mechanical refactors touch runtime code paths (providers, orchestration, text generation) by changing how/where Schema decoders/encoders are constructed and invoked; while intended to be behavior-preserving, subtle decode/encode or error-handling differences could surface at runtime. The new lint rule itself is low-risk but may introduce new warnings and enforcement expectations across the repo.

Overview
Adds a new workspace oxlint-plugin-t3code and registers it in .oxlintrc.json, introducing t3code/no-inline-schema-compile to warn when Schema.* decoder/encoder/predicate compilers are created and immediately invoked inside function bodies instead of being hoisted.

Updates the repo to oxlint@^1.63.0 (and adds @oxlint/plugins) and applies the rule’s guidance across the codebase by hoisting many Schema.decode*/Schema.encode*/Schema.is helpers to module scope and reusing them, including safer diagnostic JSON serialization paths that avoid throwing when encoding fails.

Reviewed by Cursor Bugbot for commit e9f5490. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add oxlint plugin with no-inline-schema-compile rule and hoist Schema compilers to module scope

  • Introduces a new oxlint-plugin-t3code workspace package (index.ts) with the no-inline-schema-compile rule (no-inline-schema-compile.ts) that warns when Effect Schema compiler calls (e.g. Schema.decodeUnknownEffect) are immediately invoked inside function bodies with static schemas instead of being hoisted to module scope.
  • Registers the plugin in .oxlintrc.json at warn severity and disables eslint/no-underscore-dangle.
  • Applies the pattern the rule enforces across the entire codebase: replaces hundreds of inline Schema.decodeSync(Foo)(value) / Schema.is(Foo)(value) call patterns with module-scope cached decoders and type guards.
  • Fixes a class of runtime errors in ClaudeAdapter, CursorAdapter, and text generation layers where sync schema encoding could throw on non-serializable inputs; these now return fallback strings or surface structured TextGenerationError values instead.
  • Risk: the lint rule is heuristic-based and may produce false positives for dynamic schema parameters; these are suppressed by the static-reference detection logic but edge cases may require per-site suppressions.

Macroscope summarized e9f5490.

juliusmarminge and others added 2 commits May 8, 2026 10:51
- Introduce a t3code oxlint plugin and wire it into repo linting

- Flag Effect Schema compiler calls inside function bodies

- Add fixture-based tests for valid and invalid cases

Co-authored-by: codex <codex@users.noreply.github.com>
- Switch to explicit Effect module imports
- Encode Oxlint config with Schema
- Simplify test assertions and harness signatures
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0620a8d1-0bff-4222-a6ff-15a22ca8f961

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/23154b3f

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

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels May 8, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes May 8, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented May 8, 2026

Approvability

Verdict: Approved

Scaffolds a new oxlint plugin with mechanical refactors across the codebase to hoist schema compiler calls to module scope. Changes are purely structural with no runtime behavior modifications. Unresolved comments are low-severity style concerns about the lint rule itself.

You can customize Macroscope's approvability policy. Learn more.

@juliusmarminge juliusmarminge changed the title Tighten Effect imports and add Oxlint schema compiler rule Scaffold oxlint plugin with initial rule May 8, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
@macroscopeapp macroscopeapp Bot dismissed their stale review May 8, 2026 18:31

Dismissing prior approval to re-evaluate 3dd82e2

@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels May 8, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Lint rule falsely treats dynamic member accesses as static
    • Fixed isStaticSchemaReference to traverse MemberExpression chains and only classify them as static when the root object is a capitalized identifier, preventing false positives on dynamic accesses like input.schema.

Create PR

Or push these changes by commenting:

@cursor push 6ca33a3e1a
Preview (6ca33a3e1a)
diff --git a/oxlint-plugin-t3code/rules/no-inline-schema-compile.test.ts b/oxlint-plugin-t3code/rules/no-inline-schema-compile.test.ts
--- a/oxlint-plugin-t3code/rules/no-inline-schema-compile.test.ts
+++ b/oxlint-plugin-t3code/rules/no-inline-schema-compile.test.ts
@@ -54,6 +54,16 @@
     `,
   );
 
+  rule.valid(
+    "allows dynamic member access on function parameters",
+    `
+      import { Schema } from "effect";
+
+      export const parseWith = (input: { schema: Schema.Codec<any, any> }, value: unknown) =>
+        Schema.decodeUnknownEffect(input.schema)(value);
+    `,
+  );
+
   rule.invalid(
     "reports schema compilers inside function bodies",
     `

diff --git a/oxlint-plugin-t3code/rules/no-inline-schema-compile.ts b/oxlint-plugin-t3code/rules/no-inline-schema-compile.ts
--- a/oxlint-plugin-t3code/rules/no-inline-schema-compile.ts
+++ b/oxlint-plugin-t3code/rules/no-inline-schema-compile.ts
@@ -46,16 +46,30 @@
   );
 };
 
+const isCapitalizedIdentifier = (node: unknown): boolean => {
+  const expression = unwrapExpression(node);
+  if (Option.isNone(expression) || expression.value.type !== "Identifier") return false;
+  const [firstChar] = expression.value.name;
+  return firstChar !== undefined && firstChar.toUpperCase() === firstChar;
+};
+
 const isStaticSchemaReference = (node: unknown): boolean => {
   const expression = unwrapExpression(node);
   if (Option.isNone(expression)) return false;
 
   if (expression.value.type === "Identifier") {
-    const [firstChar] = expression.value.name;
-    return firstChar !== undefined && firstChar.toUpperCase() === firstChar;
+    return isCapitalizedIdentifier(node);
   }
 
-  return expression.value.type === "MemberExpression";
+  if (expression.value.type === "MemberExpression") {
+    let current: Option.Option<{ type: string; object?: unknown }> = expression;
+    while (Option.isSome(current) && current.value.type === "MemberExpression") {
+      current = unwrapExpression(current.value.object);
+    }
+    return Option.isSome(current) && isCapitalizedIdentifier(current.value);
+  }
+
+  return false;
 };
 
 const isNestedStaticSchemaCall = (node: unknown): boolean => {

You can send follow-ups to the cloud agent here.

}

return expression.value.type === "MemberExpression";
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lint rule falsely treats dynamic member accesses as static

Low Severity

isStaticSchemaReference returns true for any MemberExpression, but not all member expressions are static/hoistable. A dynamic property access like input.schema or options.schema (where the object is a function parameter) would be incorrectly classified as a static reference. This causes the rule to warn about code that cannot actually be hoisted to module scope, since the schema is only known at call time. The test suite only covers the simple Identifier case for dynamic schemas (lowercase schema), missing this MemberExpression edge case.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3dd82e2. Configure here.

juliusmarminge and others added 2 commits May 8, 2026 11:42
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Identical helper function duplicated across three files
    • Extracted the duplicated encodeJsonStringForDiagnostics function and its encodeUnknownJsonStringExit constant into @t3tools/shared/schemaJson, then updated all three consuming files to import from the shared module.

Create PR

Or push these changes by commenting:

@cursor push a4a2302607

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit e9f5490. Configure here.

Comment thread apps/server/src/provider/Layers/ClaudeAdapter.ts
@juliusmarminge juliusmarminge merged commit 825263b into main May 8, 2026
13 checks passed
@juliusmarminge juliusmarminge deleted the t3code/23154b3f branch May 8, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant