openclaw/scripts/check.mts
Peter Steinberger 799ddbd35d
refactor(comments): deslop production narration
## What Problem This Solves

Production code still carries duplicated narration over function names, types, branches and CSS selectors. Some of that prose has drifted: Zalo polling is described as development-only even though it is the default production route, and a joined hook helper is called fire-and-forget.

## User Impact

No user-visible behavior changes. Runtime logic, templates, CSS declarations, configuration, persisted state, wire formats, public API documentation, licenses and lint-suppression reasons remain intact.

## Why This Change Was Made

This maintainer-requested cleanup removes redundant internal helper/registrar summaries, repeated section labels, and obsolete inline font-size history. Existing declarations and shared owners already express these facts; no new abstraction is needed. Comments explaining authority, lifecycle, ordering, cleanup, platform constraints, dependencies and public contracts stay.

The measured reduction is 696 net production/tooling lines: 604 standalone comment lines and 92 adjacent blank lines, plus 59 inline comment removals without net line savings. No tests or generated files changed. This is a bounded contextual sweep, not a claim of exhaustive repository coverage; the local census records exact findings, retained candidates, and unread files. Filename-header cleanup from #161768 is excluded.

## Evidence

Independent review completed; all accepted documentation findings were addressed by restoring base comments. The remaining changed files are byte-identical to the reviewed and remotely frozen candidate.

Blacksmith Testbox validation:
- Parser comparison: identical non-comment TypeScript tokens and CSS structure.
- Both import-cycle checks: 0 cycles.
- Focused existing tests: 40 Vitest shards passed (521.71 seconds).
- Plugin contracts: 48 files / 1,153 tests passed.
- Plugin, source-to-extension, and SDK/package import-boundary checks passed.
- Feishu asset hook check: no build hooks; no plugin browser/control-UI source changed.

SDK API comparison passed with no API changes. The full changed-file gate passed remotely. The lowered-threshold duplicate census (12 lines / 80 tokens) completed; its raw 155 records include deliberate probe/fixture matches and are not claimed as removable production code. No tests were added or changed.

Public JSDoc was audited independently: the SDK API comparison strips comments, while shipped declarations can preserve them, so API-shape equality alone would not prove documentation preservation.


### Inherited hosted CI failure

Exact-head [CI run 36815106181](https://github.com/openclaw/openclaw/actions/runs/36815106181) tested `ff26a4c05d3b41d25477df41cb94010c6cac5cb0` merged with main `75d1f82c18`. The only failing test job was `checks-node-compact-small-19`: `test/helpers/openclaw-test-instance.acquisition.test.ts:33`, “keeps an absent Gateway unreachable while retaining its port claims,” expected `free` but received `busy`. The other failure is the aggregate CI gate. This attempt has 84 successful jobs, 16 skipped jobs, and 88 collateral cancellations; cancelled coverage is not counted as passing.

The identical assertion and error were independently verified in [job 110052175838](https://github.com/openclaw/openclaw/actions/runs/36763489663/job/110052175838), the latest attempt (1) for unrelated PR #162070's final head `d07a6981d4`. The acquisition test, instance helper, cleanup wrapper, isolated-state writer, port allocator, claim owner, claim-lock owner and TCP probe are byte-identical between that head and this PR. No changed file participates in the failing acquisition/probe path. The failure precedes Gateway startup; the logs do not identify the competing listener, so no root-cause repair is claimed.

Landing uses the maintainer-authorized inherited-failure exception pinned to this exact head, backed by the passing remote gates above. The fixture defect remains with the main-CI coordinator. No workflow rerun, test weakening, timeout increase, or source repush was used to obtain green. GitHub's GraphQL writer rejected auto-merge because its quota was exhausted; the request was reconciled as absent before selecting the supported REST squash path.
2026-09-30 22:28:36 -07:00

218 lines
7.5 KiB
TypeScript

// Runs the repository check lanes selected by CLI arguments.
import { performance } from "node:perf_hooks";
import { booleanFlag, parseFlagArgs, stringFlag } from "./lib/arg-utils.mts";
import { printTimingSummary } from "./lib/check-timing-summary.mts";
import { runManagedCommand } from "./lib/managed-child-process.mts";
type CheckCommand = { name: string; args: string[]; usesBase?: boolean };
type RunManagedCheck = (options: { args: string[]; bin: string }) => Promise<number>;
export const PREFLIGHT_CHECKS: CheckCommand[] = [
{ name: "conflict markers", args: ["check:no-conflict-markers"] },
{ name: "script TypeScript erasability", args: ["check:script-erasability"] },
{ name: "line-cap growth ratchet", args: ["check:line-cap-ratchet"], usesBase: true },
{ name: "SQLite worker ratchet", args: ["check:database-worker-ratchet"], usesBase: true },
{ name: "max-lines suppression ratchet", args: ["check:max-lines-ratchet"], usesBase: true },
{ name: "assertion SAFETY comment ratchet", args: ["check:assertion-safety"], usesBase: true },
{ name: "test timeout race ratchet", args: ["check:test-timeout-race-ratchet"], usesBase: true },
{ name: "changelog attributions", args: ["check:changelog-attributions"] },
{ name: "database-first legacy-store guard", args: ["check:database-first-legacy-stores"] },
{ name: "doctor deprecation registry", args: ["check:doctor-deprecation-registry"] },
{
name: "guarded extension wildcard re-exports",
args: ["lint:extensions:no-guarded-wildcard-reexports"],
},
{
name: "plugin-sdk wildcard re-exports",
args: ["lint:extensions:no-plugin-sdk-wildcard-reexports"],
},
{
name: "deprecated channel access seams",
args: ["lint:extensions:no-deprecated-channel-access"],
},
{ name: "media download helper guard", args: ["check:media-download-helpers"] },
{ name: "runtime sidecar loader guard", args: ["check:runtime-sidecar-loaders"] },
{ name: "tool display", args: ["tool-display:check"] },
{ name: "host env policy", args: ["check:host-env-policy:swift"] },
{ name: "browser inspect script", args: ["check:browser-inspect-script:swift"] },
{ name: "native conversation privacy defaults", args: ["native-catalogs:check"] },
{ name: "opengrep rule metadata", args: ["check:opengrep-rule-metadata"] },
{ name: "duplicate scan target coverage", args: ["dup:check:coverage"] },
{ name: "npm package-lock guard", args: ["deps:npm-lock:check"] },
{ name: "package patch guard", args: ["deps:patches:check"] },
];
export function usage() {
return [
"Usage: node --import tsx scripts/check.mts [--base <ref>] [--timed] [--include-architecture] [--include-test-types]",
"",
"Runs the local check graph: guard preflights, typecheck, lint, and policy guards.",
"",
"Options:",
" --base <ref> Compare ratchets against the fork with this Git ref.",
" --timed Print timing summary even when checks pass.",
" --include-architecture Run architecture import-cycle checks instead of runtime cycles.",
" --include-test-types Typecheck production and test sources.",
" -h, --help Show this help.",
].join("\n");
}
function parseCheckArgs(argv: string[]) {
return parseFlagArgs(
argv,
{ base: "", help: false, includeArchitecture: false, includeTestTypes: false, timed: false },
[
stringFlag("--base", "base", { rejectShortOptions: true }),
booleanFlag("--timed", "timed", true, { repeatable: true }),
booleanFlag("--include-architecture", "includeArchitecture", true, { repeatable: true }),
booleanFlag("--include-test-types", "includeTestTypes", true, { repeatable: true }),
booleanFlag("--help", "help", true, { repeatable: true }),
booleanFlag("-h", "help", true, { repeatable: true }),
],
{
ignoreDoubleDash: false,
onUnhandledArg(arg: string) {
throw new Error(`unknown argument: ${arg}\n\n${usage()}`);
},
},
);
}
export async function main(argv = process.argv.slice(2)) {
let args;
try {
args = parseCheckArgs(argv);
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 2;
return;
}
if (args.help) {
console.log(usage());
process.exitCode = 0;
return;
}
const tailChecks = [
{ name: "webhook body guard", args: ["lint:webhook:no-low-level-body-read"] },
{ name: "runtime action config guard", args: ["check:no-runtime-action-load-config"] },
!args.includeArchitecture
? {
name: "deprecated API usage guard",
args: ["check:deprecated-api-usage"],
}
: null,
!args.includeArchitecture
? {
name: "wrapper shadowing guard",
args: ["check:wrapper-shadowing"],
}
: null,
{ name: "temp path guard", args: ["check:temp-path-guardrails"] },
{ name: "pairing store guard", args: ["lint:auth:no-pairing-store-group"] },
{ name: "pairing account guard", args: ["lint:auth:pairing-account-scope"] },
args.includeArchitecture
? { name: "architecture import cycles", args: ["check:architecture"] }
: { name: "runtime import cycles", args: ["check:import-cycles"] },
].filter((command) => command !== null);
const stages = [
{
name: "preflight guards",
parallel: true,
commands: PREFLIGHT_CHECKS.map((command) =>
command.usesBase && args.base
? { name: command.name, args: [...command.args, "--base", args.base] }
: command,
),
},
{
name: "typecheck",
parallel: false,
commands: args.includeTestTypes
? [{ name: "typecheck all", args: ["tsgo:all"] }]
: [
{ name: "typecheck prod", args: ["tsgo:prod"] },
{ name: "typecheck scripts", args: ["tsgo:scripts"] },
{ name: "typecheck test root", args: ["tsgo:test:root"] },
],
},
{
name: "lint",
parallel: false,
commands: [
{ name: "lint", args: ["lint"] },
{ name: "format", args: ["format:check"] },
],
},
{
name: "policy guards",
parallel: true,
commands: tailChecks,
},
];
const timings = [];
let exitCode = 0;
for (const stage of stages) {
console.error(`\n[check] ${stage.name}`);
const results = stage.parallel
? await Promise.all(stage.commands.map((command) => runCommand(command)))
: await runSerial(stage.commands);
timings.push(...results);
const failed = results.find((result) => result.status !== 0);
if (failed) {
exitCode = failed.status;
break;
}
}
if (args.timed || exitCode !== 0) {
printSummary(timings);
}
process.exitCode = exitCode;
}
async function runSerial(commands: CheckCommand[]) {
const results: Array<Awaited<ReturnType<typeof runCommand>>> = [];
for (const command of commands) {
const result = await runCommand(command);
results.push(result);
if (result.status !== 0) {
break;
}
}
return results;
}
export async function runCommand(
command: CheckCommand,
runManagedCommandImpl: RunManagedCheck = runManagedCommand,
) {
const startedAt = performance.now();
let status = 1;
try {
status = await runManagedCommandImpl({
args: command.args,
bin: "pnpm",
});
} catch (error) {
console.error(error);
}
return {
name: command.name,
durationMs: performance.now() - startedAt,
status,
};
}
function printSummary(timings: Array<Awaited<ReturnType<typeof runCommand>>>) {
printTimingSummary("check", timings);
}
if (import.meta.main) {
await main();
}