Skip to content

fix(vscode): keep Rslint healthy when rstack.config has no define.lint() - #94

Merged
fi3ework merged 1 commit into
mainfrom
fix/rslint-fmt-only-config
Oct 10, 2026
Merged

fi3ework merged 1 commit into
mainfrom
fix/rslint-fmt-only-config

Conversation

@fi3ework

Copy link
Copy Markdown
Member

Summary

A root rstack.config.* with only define.fmt() lights the lint stack (by design: the extension never reads the Rstack config itself). rstack's lint shim then calls process.exit(1) because it finds no define.lint(), and since the editor-shipped lint worker evaluates that shim in-process, the worker died mid-rslint/loadConfigs and took the Go server and the language-client transport with it. That is the "No lint configuration found" → connection-disposed → server-restart cascade in #93.

 lint worker (bridged)
   ConfigModuleHost
-    loadFresh(shim)              # shim calls process.exit(1) → worker dies
+    withProcessExitAsThrow(loadFresh)(shim)
+      process.exit → throws { code: RSTACK_CONFIG_PROCESS_EXIT }
+      → failed load result, worker and Go stay up
   ConfigTransactionAdapter.loadConfigs
     missing dependency → `missing`
+    RSTACK_CONFIG_PROCESS_EXIT → `unconfigured`
 editor (Rslint.ts)
   rstack/rslintConfigDependency
     missing → disabled (poll)
     plugin  → disabled (poll)
+    unconfigured → running, detail "no define.lint() in rstack.config.ts", one info line
     error   → crashed

Behaviour for a formatting-only project: Rslint still starts (the shim is the only thing that can decide), reports running with a detail, logs one info line, and produces no diagnostics, no toast, no crashed, and no 60-second dependency poll. Adding define.lint() later is picked up by the same worker through the existing bridged rstack.config.* watcher, without a restart. Native runtimes build their ConfigModuleHost exactly as before.

Why not "don't start Rslint at all": the extension cannot know whether define.lint() exists without evaluating the config, and a text probe is forbidden (AGENTS.md) and wrong under shared config layers. The shim's refusal is currently expressed only as process.exit(1); once rstack exports a marker or throws a typed error instead, the wrapper can go and the editor can classify by contract.

Known trade-offs, recorded in packages/vscode/AGENTS.md:

  • Any process.exit during a bridged config load (including one from the user's own config) is classified unconfigured. With rstack 0.8.2 the shim's refusal is the only exit in its import graph. A tighter check (exit code 1 + caller is the shim path) is possible as a follow-up.
  • After a catalog with define.lint() has committed, removing it makes Go keep the last-good catalog while the status says no define.lint(); rs lint would refuse.
  • The shim's own No lint configuration found… line and Go's Skipped config … line still reach the Rslint Output channel beside our info line.

Evidence

  • Before: the new Rstack fmt-only config E2E suite, run against dist/ built from the pre-fix source, fails with the issue's error:
    Rslint became crashed while waiting for the unconfigured running detail:
    Pending response rejected since connection got disposed (…/@rslint/core)
    
    After: pnpm lint ✓ · pnpm test:unit 365 passed, 0 failed · lint E2E (Rstack fmt-only config, Missing config dependency, Rstack lint bridge, JS config tests) all Exit code: 0 on macOS with VSCODE_CLI=1. The fmt-only suite asserts no crashed state, no diagnostics, no warnings, then adds define.lint() and asserts no-debugger appears from the same lint-worker pid.
  • Manual check in a clean VS Code profile with the packaged VSIX on a define.fmt()-only project (rstack 0.8.2): status bar hover shows running — no define.lint() in rstack.config.ts, the Output channel has the single info line and no connection-disposed or restart errors; uncommenting define.lint(...) and saving produces the diagnostic without a restart.

Merge Danger

Door: two-way

Revert restores the previous behaviour; no settings, protocol versions or published surfaces change. The new unconfigured verdict is editor-internal (worker ↔ extension host).

Blast Radius: lint-bridged

Only bridged lint runtimes (a root rstack.config.* without a tool-native rslint.config.*) take the new path, and only when the shim refuses. Native Rslint runtimes, Rstest and fmt are untouched.

Related Links

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).

rstack's lint shim (dist/rslintConfig.js) calls process.exit(1) when the
Rstack config defines no lint configuration. The editor's lint worker
evaluates that shim in-process, so a formatting-only rstack.config.* killed
the worker mid-transaction and took the Go server and the language client
transport down with it: "No lint configuration found" followed by
connection-disposed and server-restart errors on every TypeScript file.

The bridged worker now builds its ConfigModuleHost with a loadFresh wrapped
by withProcessExitAsThrow: during module evaluation, process.exit throws an
error tagged RSTACK_CONFIG_PROCESS_EXIT, which lands as a failed load result.
ConfigTransactionAdapter classifies that code as a new `unconfigured` verdict
on rstack/rslintConfigDependency, beside `missing` and `plugin`. The editor
reports `running` with the detail `no define.lint() in rstack.config.ts`,
logs one info line per episode, and neither polls, crashes nor warns. Adding
define.lint() later is picked up by the same worker through the bridged
config watcher, without a restart. Native runtimes are untouched.

Adds the rstack-fmt-only E2E fixture and suite, unit coverage for the
wrapper, the verdict and the editor state, and records the verdict and the
lint-only shim behaviour in packages/vscode/AGENTS.md.

Closes #93
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-10T03:58:16.955448Z 9119895 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@fi3ework
fi3ework merged commit 5514572 into main Oct 10, 2026
3 checks passed
@fi3ework
fi3ework deleted the fix/rslint-fmt-only-config branch October 10, 2026 05:52
fi3ework added a commit that referenced this pull request Oct 10, 2026
…95)

Follow-up to #94 (#93). A bridged lint runtime whose root rstack.config.*
has no define.lint() reported `running` with a detail, which the status
bar hover rendered as a green check beside Rslint with the reason only
in the icon tooltip. The status bar's word for "this tool has no
configuration here" is `not-detected`, so the `unconfigured` verdict now
maps to `{ kind: 'not-detected', detail }` and the reason is shown as a
hover notice row.

- `StackState['not-detected']` gains an optional `detail`; only a
  runtime-level "nothing configured" verdict sets it, detection never
  does, so every other hover is unchanged.
- The status bar spells out the `not-detected` detail; one shared
  `stackStateDetail` replaces the two identical switches in the shell
  and the lint fold.
- `liveRslintStatus` / `reportLive` (renamed from `running*`) keep the
  advisory-wins precedence; the worker stays up, no poll, no toast.
- Fold: an unconfigured-only folder stays `not-detected` with its
  reason; a healthy native runtime beside it wins.
- AGENTS.md adaptation 7 and the lint-shim gotcha record the design,
  including that the verdict lives with the runtime (an idle folder
  reads `running: idle` until a document opens).
@fi3ework fi3ework mentioned this pull request Oct 10, 2026
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.

Rslint starts and fails for a formatting-only Rstack config

1 participant