chore(lint): enforce reviewed promise and fallback contracts

This commit is contained in:
morluto committed 2026-10-25 08:29:20 +08:00
1 parent 5938581b37
commit 627c085cee
4 files changed
+67 -16

No files matched your search

+26 -6
View File
@@ -50,13 +50,13 @@
"rea/no-evidence-json-preadmission": "error",
"rea/no-positional-tool-contract": "error",
"rea/no-fixture-root-cleanup": "error",
"typescript/no-floating-promises": "warn",
"typescript/no-floating-promises": "error",
"typescript/no-misused-promises": "warn",
"typescript/return-await": ["warn", "error-handling-correctness-only"],
"typescript/return-await": ["error", "error-handling-correctness-only"],
"typescript/switch-exhaustiveness-check": [
"warn",
"error",
{
"considerDefaultExhaustiveForUnions": false
"considerDefaultExhaustiveForUnions": true
}
]
},
@@ -148,9 +148,29 @@
}
},
{
"files": ["src/**/*.test.ts"],
"files": [
"scripts/lib/http3-capture-assertions.test.mjs",
"scripts/lib/http3-verifier-lifecycle.test.mjs"
],
"rules": {
"typescript/no-floating-promises": "error"
"typescript/no-floating-promises": [
"error",
{
"allowForKnownSafeCalls": [
{
"from": "package",
"package": "@types/node",
"name": "test"
}
]
}
]
}
},
{
"files": ["scripts/fixtures/javascript-recovery/code-split/entry.mjs"],
"rules": {
"typescript/no-floating-promises": "off"
}
}
],
+13 -3
View File
@@ -169,14 +169,24 @@ deletion; mutable bindings and mixed root arrays remain outside its scope.
Focused `.only` declarations, broken assertion promise chains, and unused disable
directives are errors. `vitest/valid-expect` warns (`maxArgs: 2`) because valid stored and
collected assertions can be awaited later. `import/no-duplicates` warns with
`preferInline: false`, preserving separate type imports.
`preferInline: false`.
Fast lint stays syntax-based. `npm run lint:typed` runs serially with pinned
`oxlint-tsgolint` 7.0.2005 and records native plus typed diagnostics in
`.cache/static-checks/typed-oxlint.json`. Production `src` treats
`no-misused-promises` as an error; tests/fixtures warn. `no-floating-promises` is
an error in `src/**/*.test.ts`; other scopes, return-await and exhaustiveness
findings warn pending triage.
an error throughout the lint inventory. `return-await` is an error in
error-handling scopes so asynchronous completion stays inside its catch or
cleanup boundary. Exhaustiveness is an error when a union member has neither a
case nor a default. Intentional defaults count as handling the remaining union;
new members covered by those defaults therefore require behavioral review.
Two HTTP/3 source-test files allow the runner-owned `node:test` registration API;
ordinary unowned promises in those files still fail. The authored code-split
recovery input is exempt from floating-promise policy because it is analysis
input, rather than application orchestration. Preserve framework-owned callbacks
such as an awaited `Promise.finally`; inspect the actual completion owner before
changing a callback solely to satisfy its declared `void` return type.
`npm run knip:production` writes advisory candidates to
`.cache/static-checks/production-reachability.json`; execution/report errors fail.
@@ -60,12 +60,28 @@ export function promiseCondition(): string {
"src/nonexhaustive.ts": `
type Outcome = { kind: "open" } | { kind: "closed" };
export function describeOutcome(outcome: Outcome): string {
switch (outcome.kind) {
case "open": return "open";
}
return "unknown";
}
`,
"src/with-fallback.ts": `
type Outcome = { kind: "open" } | { kind: "closed" };
export function describeOutcome(outcome: Outcome): string {
switch (outcome.kind) {
case "open": return "open";
default: return "unknown";
}
}
`,
"scripts/lib/http3-capture-assertions.test.mjs": `
import { test as registerTest } from "node:test";
registerTest("runner owns this registration", async () => {
await Promise.resolve();
});
Promise.resolve("ordinary unowned promise remains visible");
`,
"src/unawaited-finally.ts": `
export async function cleanupBeforeCompletion(): Promise<string> {
@@ -85,10 +101,10 @@ it("keeps test-only promise conditions advisory", () => {
"tests/unjoined.test.ts": `
import { expect, it } from "vitest";
const cliTest = it.extend<{ label: string }>({ label: "owned" });
it("warns about an unjoined boundary-test assertion", () => {
it("rejects an unjoined boundary-test assertion", () => {
expect(Promise.resolve("ready")).resolves.toBe("ready");
});
cliTest("warns in an extended boundary-test callback", ({ label }) => {
cliTest("rejects an unjoined extended boundary-test assertion", ({ label }) => {
expect(Promise.resolve(label)).resolves.toBe("owned");
});
`,
@@ -116,6 +132,8 @@ processTest(
JSON.stringify({
compilerOptions: {
strict: true,
allowJs: true,
checkJs: true,
noEmit: true,
skipLibCheck: true,
module: "NodeNext",
@@ -176,17 +194,18 @@ processTest(
)
.sort(),
).toEqual([
"scripts/lib/http3-capture-assertions.test.mjs: error: typescript(no-floating-promises)",
"src/misused.ts: error: typescript(no-misused-promises)",
"src/nonexhaustive.ts: warning: typescript(switch-exhaustiveness-check)",
"src/nonexhaustive.ts: error: typescript(switch-exhaustiveness-check)",
"src/test-misused.test.ts: warning: typescript(no-misused-promises)",
"src/unawaited-finally.ts: warning: typescript(return-await)",
"src/unawaited-finally.ts: error: typescript(return-await)",
"src/unjoined.test.ts: error: typescript(no-floating-promises)",
"src/unjoined.test.ts: error: typescript(no-floating-promises)",
"tests/unjoined.test.ts: warning: typescript(no-floating-promises)",
"tests/unjoined.test.ts: warning: typescript(no-floating-promises)",
"tests/unjoined.test.ts: error: typescript(no-floating-promises)",
"tests/unjoined.test.ts: error: typescript(no-floating-promises)",
]);
expect(
diagnostics.filter(({ severity }) => severity === "error"),
).toHaveLength(3);
).toHaveLength(8);
},
);
+2
View File
@@ -133,6 +133,8 @@ export const reconstructionProofApplicabilityCases = () =>
);
const proof = result.bindings[0]!;
switch (specification.name) {
case "matching implementation and revision":
break;
case "stronger proof authority":
input.manifest.bindings[0]!.fixtures[0]!.authority = "integration";
input.manifest.bindings[0]!.verifier!.authority = "integration";