fix: auto-rebuild workspace packages when src is newer than dist

Without this, edits to packages/coding-agent/src/* (or any other workspace
package src) silently land while the dist stays stale — agents continue
loading the old compiled JS and operators see "why didn't my edit take
effect?" symptoms. Observed 2026-05-17 wiring in the AST tools: vitest
(reading TS source) passed; runtime smoke test against dist failed because
no auto-rebuild fired.

Extends ensure-source-resources.cjs (which sf-from-source runs on every
launch) to also check workspace packages: agent-core, ai, coding-agent,
daemon, google-gemini-cli-provider, openai-codex-provider, rpc-client, tui.
For each, compare latest src mtime vs latest dist mtime (with a 100ms grace
window). If src is newer, run `npm run build -w @singularity-forge/<pkg>`.

Excludes:
  - packages/native (Rust build is 5–10 min; trigger manually via
    `node rust-engine/scripts/build.js --dev`).
  - Any package in SF_SKIP_WORKSPACE_AUTOBUILD (comma-separated).
  - Whole step disabled by SF_SKIP_WORKSPACE_AUTOBUILD=all.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mikael Hugo 2026-05-17 17:19:25 +02:00
parent 6e3b3d3c54
commit c046ff9a6c

View file

@ -90,3 +90,71 @@ if (shouldRebuild()) {
process.exit(result.status ?? 1);
}
}
/**
* Auto-rebuild workspace packages whose src/ is newer than dist/.
*
* Without this, edits to packages/coding-agent/src/* (or any other workspace
* package src) silently land while the dist stays stale agents continue
* loading the old compiled JS and operators see "why didn't my edit take
* effect?" symptoms (observed 2026-05-17 with the AST tools wire-in).
*
* Skipped:
* - native: Rust build takes 510 min; trigger manually via
* `node rust-engine/scripts/build.js --dev` instead.
* - any package set in SF_SKIP_WORKSPACE_AUTOBUILD (comma-separated names).
*
* Override the whole step with SF_SKIP_WORKSPACE_AUTOBUILD=all.
*/
const WORKSPACE_AUTOBUILD_PACKAGES = [
"agent-core",
"ai",
"coding-agent",
"daemon",
"google-gemini-cli-provider",
"openai-codex-provider",
"rpc-client",
"tui",
];
function packageNeedsRebuild(pkgName) {
const pkgDir = join(root, "packages", pkgName);
const pkgSrc = join(pkgDir, "src");
const pkgDist = join(pkgDir, "dist");
if (!existsSync(pkgSrc) || !existsSync(pkgDist)) return false;
const srcMtime = latestMtimeMs(pkgSrc);
const distMtime = latestMtimeMs(pkgDist);
// Add a small grace window so files written within the same second as a
// dist sync don't trigger redundant rebuilds.
return srcMtime > distMtime + 100;
}
function rebuildWorkspacePackagesIfStale() {
const skip = (process.env.SF_SKIP_WORKSPACE_AUTOBUILD || "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
if (skip.includes("all")) return;
const stale = WORKSPACE_AUTOBUILD_PACKAGES.filter(
(pkg) => !skip.includes(pkg) && packageNeedsRebuild(pkg),
);
if (stale.length === 0) return;
console.error(
`[forge] Workspace src newer than dist for: ${stale.join(", ")} — rebuilding...`,
);
for (const pkg of stale) {
const result = spawnSync(
"npm",
["run", "build", "-w", `@singularity-forge/${pkg}`],
{ cwd: root, stdio: "inherit", env: process.env },
);
if (result.status !== 0) {
console.error(`[forge] FAILED to rebuild packages/${pkg}`);
process.exit(result.status ?? 1);
}
}
}
rebuildWorkspacePackagesIfStale();