Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,13 @@ screenshots/**/*.png filter=lfs diff=lfs merge=lfs -text

# FNXC:RepoAssets 2026-08-10-10:00: Graph JSON is byte-stable across platforms only when Git retains LF endings.
.fusion-knowledge/**/*.json text eol=lf

# FNXC:PreJsonMerge 2026-09-23: .changeset/pre.json is generated by changesets and must always follow
# the branch being merged INTO — the exact rule scripts/check-pre-json-anchor.mjs prescribes manually.
# A merge/rebase conflict resolved from the older side silently breaks the beta anchor and blocks
# verification for every task on that branch (observed 2026-09-23: two review tasks parked
# terminal-failed 8-9h; same regression class as #3472). merge=binary is a built-in git driver that
# keeps the INTO-side copy and needs no per-clone configuration (the common merge=ours recipe requires
# `git config merge.ours.driver true` in every clone and otherwise falls back to full conflict
# markers). The check stays as detection; this rule prevents the corruption at resolution time.
.changeset/pre.json merge=binary
14 changes: 12 additions & 2 deletions .github/workflows/agent-browser-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ jobs:
# contract; post-job cache save otherwise fails). The composite's skip-install
# input means the caller installs workspace deps; build the CLI before
# `pnpm pack` so the pack guard stays intact as a real published-boundary test.
timeout-minutes: 15
# FNXC:PackFixtureTimeout 2026-09-24-14:11:
# ensureDesktopRuntimeAssetsBuilt() gives the @fusion/desktop sub-build a 30-minute
# budget (packages/cli/tsup.config.ts). The old 15-minute cap was BELOW that documented
# minimum and killed the pack-fixture with SIGTERM ("The operation was canceled").
# Matches release.yml (widened to 30 for exactly this reason). Fix (B) - credit: Hermes.
timeout-minutes: 30

steps:
- name: Checkout
Expand Down Expand Up @@ -79,7 +84,12 @@ jobs:
name: Agent-browser install (${{ matrix.os }})
needs: pack-fixture
runs-on: ${{ matrix.os }}
timeout-minutes: 15
# FNXC:PackFixtureTimeout 2026-09-24-14:11:
# ensureDesktopRuntimeAssetsBuilt() gives the @fusion/desktop sub-build a 30-minute
# budget (packages/cli/tsup.config.ts). The old 15-minute cap was BELOW that documented
# minimum and killed the pack-fixture with SIGTERM ("The operation was canceled").
# Matches release.yml (widened to 30 for exactly this reason). Fix (B) - credit: Hermes.
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"build:package": "cross-env FUSION_CLI_FULL_PACKAGE=1 tsup",
"build:exe": "bun run build.ts",
"build:exe:all": "bun run build.ts --all",
"typecheck": "tsc --noEmit",
"typecheck": "tsc --build --noEmit",
"test": "vitest run --silent=passed-only --reporter=dot",
"test:ci-shape": "vitest run src/__tests__/ci-workflow.test.ts --silent=passed-only --reporter=dot",
"test:docs-index": "vitest run src/__tests__/docs-readme-index.test.ts --silent=passed-only --reporter=dot",
Expand Down Expand Up @@ -126,4 +126,4 @@
"engines": {
"node": ">=22.4.0"
}
}
}
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
],
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"typecheck": "tsc --build --noEmit",
"test": "vitest run --silent=passed-only --reporter=dot",
"test:embedded-postgres": "vitest run src/__tests__/postgres/embedded-lifecycle.test.ts --silent=passed-only --reporter=dot",
"test:pg-gate": "FUSION_PG_TEST_SETUP_PARTICIPANT=1 vitest run --config vitest.pg.config.ts src/__tests__/postgres/handoff-to-review-atomicity.pg.test.ts src/__tests__/postgres/task-lifecycle-e2e.pg.test.ts --silent=passed-only --reporter=dot",
Expand Down Expand Up @@ -93,4 +93,4 @@
"optionalDependencies": {
"keytar": "^7.9.0"
}
}
}
4 changes: 2 additions & 2 deletions packages/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"test:idle-animations": "node --test scripts/idle-animation-smoke.mjs",
"test:touch-geometry": "vitest run --project dashboard-browser-touch --silent=passed-only --reporter=dot",
"test:build": "pnpm build && FUSION_DASHBOARD_DEEP=1 vitest run --project dashboard-app --project dashboard-api --silent=passed-only --reporter=dot app/__tests__/build-output.test.ts src/__tests__/plugin-registry-dist.test.ts",
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.app.json",
"typecheck": "tsc --build --noEmit && tsc --build --noEmit tsconfig.app.json",
"test:quality:api:backfill-1": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-api-quality-backfill --silent=passed-only --reporter=dot --shard=1/2",
"test:quality:api:backfill-2": "node scripts/run-vitest-with-heap.mjs --heap=6144 run --project dashboard-api-quality-backfill --silent=passed-only --reporter=dot --shard=2/2"
},
Expand Down Expand Up @@ -180,4 +180,4 @@
"vitest": "^4.1.10"
},
"private": true
}
}
4 changes: 2 additions & 2 deletions packages/engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
],
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"typecheck": "tsc --build --noEmit",
"test": "vitest run --silent=passed-only --reporter=dot --project=engine-default --project=engine-reliability",
"test:core": "vitest run --silent=passed-only --reporter=dot --project=engine-core",
"test:slow": "vitest run --silent=passed-only --reporter=dot --project=engine-slow",
Expand Down Expand Up @@ -60,4 +60,4 @@
"vitest": "^4.1.10"
},
"private": true
}
}
4 changes: 2 additions & 2 deletions packages/plugin-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
],
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit --skipLibCheck",
"typecheck": "tsc --build --noEmit",
"test": "vitest run --silent=passed-only --reporter=dot"
},
"devDependencies": {
Expand All @@ -41,4 +41,4 @@
"node": ">=22.5.0"
},
"private": true
}
}
16 changes: 16 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ ignoredBuiltDependencies:
- keytar
- sharp
- ssh2
# FNXC:WorkspaceConcurrency 2026-09-23: Cap a single `pnpm -r` run at 2 packages so its tsc workers
# (500MB-1.4GB each) cannot thrash a 12GB desktop. pnpm 10.33 ignores the ~/.npmrc workspace-concurrency
# key entirely (it echoes the value but behavior stays at the 4-project default); this key is the live
# channel, verified with a process-overlap bench (peak 4 -> 2).
# Scope limit (measured 2026-09-23): this only bounds ONE `pnpm -r` invocation. verify:fast spawns one
# `pnpm --filter <pkg> typecheck` process per changed package and bypasses it — that 4-wide fan-out is
# what froze the desktop twice (PSI memory full 80%), and it is bounded by TYPECHECK_PARALLEL_LIMIT in
# scripts/verify-fast.mjs. Do not treat this key as the fan-out guard.
# Why it lives here as a constant: pnpm 10 has no live env channel for this knob (~/.npmrc,
# npm_config_workspace_concurrency and pnpm_config_* are all silently ignored), and this manifest is
# the only backstop for an ad-hoc `pnpm -r` that no script wraps. Treat it as a blunt guardrail for
# small boxes, not a project requirement — override per command with --workspace-concurrency on a
# larger machine. Callers this repo owns already do the better thing: scripts/test-changed.mjs derives
# the value from FUSION_TEST_WORKSPACE_CONCURRENCY and passes the flag explicitly.
workspaceConcurrency: 2

onlyBuiltDependencies:
- '@embedded-postgres/darwin-arm64'
- '@embedded-postgres/darwin-x64'
Expand Down
16 changes: 13 additions & 3 deletions scripts/dev-with-memory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ import {
resolvePrebuildMode,
} from "./dev-with-memory-lib.mjs";
import { existsSync as fsExistsSync, mkdirSync as fsMkdirSync } from "node:fs";
import { totalmem as osTotalmem } from "node:os";
import { spawnSync } from "node:child_process";
import { join as pathJoin, resolve as pathResolve } from "node:path";
import { createDevSourceWatcher } from "./lib/dev-source-watch.mjs";
import { resolveDevTunnelAuth, startDevTunnel } from "./lib/dev-tunnel.mjs";

// Set increased heap size (8GB) to prevent OOM during initial build/start
const MEMORY_MB = process.env.FUSION_DEV_MEMORY_MB || "8192";
// Set increased heap size to prevent OOM during initial build/start.
// FNXC:DevMemoryCap 2026-09-23-23:34: the old `|| "8192"` was sized after one workstation, so a 12GB
// box handed every node child an 8GB heap ceiling and V8 would happily hold it. Derive the ceiling
// from the machine (half of RAM, clamped to 1-8GB) and keep FUSION_DEV_MEMORY_MB as the override.
const MEMORY_MB = process.env.FUSION_DEV_MEMORY_MB
|| String(Math.min(8192, Math.max(1024, Math.floor(osTotalmem() / 1024 / 1024 / 2))));

// Spawn the actual dev command with all arguments passed through
const { spawn } = await import("child_process");
Expand All @@ -43,7 +48,12 @@ let { watchSource } = parsedArgs;

// NODE_OPTIONS is shared with every spawned node process (build + run +
// agents). Heap size belongs here. Inspector flags do NOT — see comment above.
const nodeOptions = `--max-old-space-size=${MEMORY_MB} ${process.env.NODE_OPTIONS || ""}`.trim();
// Never emit the flag twice: callers such as ~/.fusion/start-capped.sh already pin a tighter
// `--max-old-space-size`, and precedence between repeated V8 flags is not worth relying on.
const inheritedNodeOptions = process.env.NODE_OPTIONS || "";
const nodeOptions = inheritedNodeOptions.includes("--max-old-space-size")
? inheritedNodeOptions
: `--max-old-space-size=${MEMORY_MB} ${inheritedNodeOptions}`.trim();
process.env.NODE_OPTIONS = nodeOptions;

// In dev we bind the dashboard to 0.0.0.0 so the server is reachable from
Expand Down
12 changes: 11 additions & 1 deletion scripts/verify-fast.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,17 @@ const cpuCount = (() => {
try { return Math.max(1, os.cpus()?.length ?? 1); } catch { return 1; }
})();
export const STATIC_CHECK_PARALLEL_LIMIT = Math.max(2, Math.min(8, cpuCount - 1));
export const TYPECHECK_PARALLEL_LIMIT = Math.max(1, Math.min(4, Math.floor(cpuCount / 2)));
/* FNXC:TestInfrastructure 2026-09-23-21:45: The old cap of 4 came from core count only, and on a
16-core/12GB desktop verify:fast scheduled 4 concurrent `pnpm --filter <pkg> typecheck` workers at
1-1.4GB RSS each. Measured 2026-09-23: that 4-wide batch sat in D state at 3-7% CPU per worker for
10+ minutes (swap thrash, no progress), drove PSI memory full to 80%, and froze the desktop twice.
Each tsc is memory-bound, not core-bound, so the ceiling derives from RAM (1 worker per 5GB, capped
at 4) rather than core count. This is the ONLY guard over that fan-out — `workspaceConcurrency` in
pnpm-workspace.yaml caps a single `pnpm -r` run, but these are separate pnpm processes per package
and bypass it entirely.
FUSION_VERIFY_FAST_TYPECHECK_CONCURRENCY overrides the derived default (sibling of
FUSION_VERIFY_FAST_SERIAL) so a bigger box is never slowed down by this desktop's ceiling. */
export const TYPECHECK_PARALLEL_LIMIT = Number(process.env.FUSION_VERIFY_FAST_TYPECHECK_CONCURRENCY) || Math.max(1, Math.min(4, Math.floor(os.totalmem() / (5 * 1024 ** 3))));

/**
* Build the scoped typecheck step for a package. Prefers the package's own
Expand Down
Loading