basquetWi + New ticket
pluto PLUTO-492

Lint guard: forbid non-async-function exports in "use server" modules (gitpush-pre)

Done low cpcoder-pluto-cc

Fast-follow from PLUTO-491 root cause. Next server-action transform runtime-registers EVERY export in a 'use server' file; a type-only or non-function export erases at compile -> dangling runtime binding -> ReferenceError at module load, 500ing every route in that module's import graph (491 = /practicas/[id] via SupportActionResult re-export). Add a gitpush-pre.sh lint: any file with 'use server' directive whose exports include a non-async-function (type re-export, const, etc.) FAILS the push. Prevents the whole class. Low priority — 491 already fixed the live instance.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 2026-07-03
  • wi-cli-venus commented · 2026-07-03
    LINT SPEC refined (reconciled w/ mars MARS-397 + venus, verified against 243d5b0 diff): do NOT blanket-exempt type-only exports — that misses the exact 491 bug (a genuinely type-only brace re-export that Turbopack's use-server transform still runtime-registered). Correct detector: in a 'use server' file, FLAG all brace re-exports + star ('export (type )?{…}', 'export *') regardless of type keyword, PLUS value exports (const/let/enum/class/non-async-fn); EXEMPT type DECLARATIONS ('export type X =', 'export interface X'). venus's grep 'export (type )?{…}'/'export *' is the canonical detector (proved venus clean, all 73 type exports are declarations). mars baked the type-declaration exemption into MARS-397; steal cross-project.
  • wi-cli-venus note · 2026-07-17
    Detector-design finding (current-tree scan 2026-07-17): the naive 'flag all value exports (const/let/enum/class)' rule from the canonical spec would FALSE-POSITIVE on 3 live, working exports in src/db/queries.ts: export const getUserById/getUserByAuthId/getActivePeriodo = cache(async ...). These are REAL runtime value bindings (React cache() wrapping an async server action), not elided type re-exports — they've run in prod for months. The actual runtime-danger class (PLUTO-491) is ELIDED bindings only: brace re-exports (export (type )?{...}) and star (export * from) whose specifier erases at compile → dangling registration. So the guard must flag brace-re-export + star (regardless of type kw) and genuinely-non-function value exports, but MUST exempt (a) type declarations export type X=/export interface, AND (b) export const NAME = cache(async...) / export const NAME = <async arrow> — a value that resolves to a real binding. Recommend: detect brace-re-export/star as the hard rule (that's the confirmed bug), treat bare 'export const/let =' as a WARN or exempt cache()/async-arrow forms. All other current use-server non-async exports are export type/interface declarations = safe. Not yet built — needs the cache()-exemption baked in or it blocks queries.ts.
  • wi-cli-venus assigned · 7w ago
    coder-pluto-cc
  • wi-cli-venus completed · 7w ago
    v2.23.67. scripts/lint-use-server-exports.mjs + hard-fail wiring in .gitpush-pre.sh (before the CR 67 lint). Key correction to the WI's own framing: the rule is NOT 'every export must be an async function'. Measured first - 23 use-server modules hold 241 async-function exports, 69 in-file 'export type X = {...}' / 'export interface X' DECLARATIONS, and 1 'export const g = cache(async ...)'. Those 69 are live in prod and harmless: a declaration erases with no binding left behind. What actually broke PLUTO-491 was a BRACED type RE-EXPORT ('export type { SupportActionResult } from ...'), which erases to a dangling runtime binding. So the deny table is: braced export/re-export list, star re-export, default export, sync function export, non-async const/let/var (cache(async) and = async allowed). Opt-out token 'use-server-export-allow: <reason>' on the line above, matching the nsf-allow convention. Mutation-checked before wiring: appended the literal 491 line to support-actions.ts and the lint failed on it, then restored. 8 tests (src/lib/use-server-export-lint.test.ts) execute the real script against synthetic modules in a temp tree - a lint that never runs against a violating file prints OK whether or not its matcher works. Current tree passes at 23/23 modules; suite 822 green.
chore
2026-07-03 by wi-cli-venus
7w ago
2026-08-22 15:01