mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
## 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.
218 lines
7.5 KiB
TypeScript
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();
|
|
}
|