diff --git a/Gulpfile.js b/Gulpfile.js
index df8a832426dda..3616e25ee80af 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -488,29 +488,28 @@ gulp.task(
     "Runs 'local'",
     ["local"]);
 
-gulp.task(
-    "watch-diagnostics",
-    /*help*/ false,
-    [processDiagnosticMessagesJs],
-    () => gulp.watch([diagnosticMessagesJson], [diagnosticInformationMapTs, builtGeneratedDiagnosticMessagesJson]));
-
 gulp.task(
     "watch-lib",
     /*help*/ false,
     () => gulp.watch(["src/lib/**/*"], ["lib"]));
 
+const watchTscPatterns = [
+    "src/tsconfig-base.json",
+    "src/lib/**/*",
+    "src/compiler/**/*",
+    "src/tsc/**/*",
+];
 gulp.task(
     "watch-tsc",
     /*help*/ false,
-    ["watch-diagnostics", "watch-lib"].concat(useCompilerDeps),
-    () => project.watch(tscProject, { typescript: useCompiler }));
+    useCompilerDeps,
+    () => gulp.watch(watchTscPatterns, ["tsc"]));
 
 const watchServicesPatterns = [
     "src/compiler/**/*",
     "src/jsTypings/**/*",
     "src/services/**/*"
 ];
-
 gulp.task(
     "watch-services",
     /*help*/ false,
@@ -522,39 +521,49 @@ const watchLsslPatterns = [
     "src/server/**/*",
     "src/tsserver/tsconfig.json"
 ];
-
 gulp.task(
     "watch-lssl",
     /*help*/ false,
     () => gulp.watch(watchLsslPatterns, ["lssl"]));
 
-gulp.task(
-    "watch-server",
-    /*help*/ false,
-    ["watch-diagnostics", "watch-lib"].concat(useCompilerDeps),
-    () => project.watch(tsserverProject, { typescript: useCompiler }));
-
-gulp.task(
-    "watch-runner",
-    /*help*/ false,
-    useCompilerDeps,
-    () => project.watch(testRunnerProject, { typescript: useCompiler }));
-
+const watchLocalPatterns = [
+    "src/tsconfig-base.json",
+    "src/lib/**/*",
+    "src/compiler/**/*",
+    "src/tsc/**/*",
+    "src/services/**/*",
+    "src/jsTyping/**/*",
+    "src/server/**/*",
+    "src/tsserver/**/*",
+    "src/typingsInstallerCore/**/*",
+    "src/harness/**/*",
+    "src/testRunner/**/*",
+];
 gulp.task(
     "watch-local",
     "Watches for changes to projects in src/ (but does not execute tests).",
-    ["watch-lib", "watch-tsc", "watch-services", "watch-server", "watch-runner", "watch-lssl"]);
+    () => gulp.watch(watchLocalPatterns, "local"));
 
+const watchPatterns = [
+    "src/tsconfig-base.json",
+    "src/lib/**/*",
+    "src/compiler/**/*",
+    "src/services/**/*",
+    "src/jsTyping/**/*",
+    "src/server/**/*",
+    "src/tsserver/**/*",
+    "src/typingsInstallerCore/**/*",
+    "src/harness/**/*",
+    "src/testRunner/**/*",
+];
 gulp.task(
     "watch",
     "Watches for changes to the build inputs for built/local/run.js, then runs tests.",
-    ["build-rules", "watch-runner", "watch-services", "watch-lssl"],
+    ["build-rules"],
     () => {
         const sem = new Semaphore(1);
 
-        gulp.watch([runJs, typescriptDts, tsserverlibraryDts], () => {
-            runTests();
-        });
+        gulp.watch(watchPatterns, () => { runTests(); });
 
         // NOTE: gulp.watch is far too slow when watching tests/cases/**/* as it first enumerates *every* file
         const testFilePattern = /(\.ts|[\\/]tsconfig\.json)$/;
diff --git a/scripts/build/options.js b/scripts/build/options.js
index e9e3bfb7b1bc6..ba1b669188dc5 100644
--- a/scripts/build/options.js
+++ b/scripts/build/options.js
@@ -34,7 +34,7 @@ module.exports = minimist(process.argv.slice(2), {
         workers: process.env.workerCount || os.cpus().length,
         failed: false,
         keepFailed: false,
-        lkg: false,
+        lkg: true,
         dirty: false
     }
 });
diff --git a/scripts/build/project.js b/scripts/build/project.js
index 6c1ac0f3e04e2..fb193907bbe11 100644
--- a/scripts/build/project.js
+++ b/scripts/build/project.js
@@ -14,71 +14,14 @@ const ts = require("../../lib/typescript");
 const del = require("del");
 const needsUpdate = require("./needsUpdate");
 const mkdirp = require("./mkdirp");
-const prettyTime = require("pretty-hrtime");
 const { reportDiagnostics } = require("./diagnostics");
-const { CountdownEvent, ManualResetEvent } = require("prex");
+const { CountdownEvent, ManualResetEvent, Semaphore } = require("prex");
 
 const workStartedEvent = new ManualResetEvent();
 const countdown = new CountdownEvent(0);
 
-class CompilationGulp extends gulp.Gulp {
-    /**
-     * @param {boolean} [verbose]
-     */
-    fork(verbose) {
-        const child = new ForkedGulp(this.tasks);
-        child.on("task_start", e => {
-            if (countdown.remainingCount === 0) {
-                countdown.reset(1);
-                workStartedEvent.set();
-                workStartedEvent.reset();
-            }
-            else {
-                countdown.add();
-            }
-            if (verbose) {
-                log('Starting', `'${chalk.cyan(e.task)}' ${chalk.gray(`(${countdown.remainingCount} remaining)`)}...`);
-            }
-        });
-        child.on("task_stop", e => {
-            countdown.signal();
-            if (verbose) {
-                log('Finished', `'${chalk.cyan(e.task)}' after ${chalk.magenta(prettyTime(/** @type {*}*/(e).hrDuration))} ${chalk.gray(`(${countdown.remainingCount} remaining)`)}`);
-            }
-        });
-        child.on("task_err", e => {
-            countdown.signal();
-            if (verbose) {
-                log(`'${chalk.cyan(e.task)}' ${chalk.red("errored after")} ${chalk.magenta(prettyTime(/** @type {*}*/(e).hrDuration))} ${chalk.gray(`(${countdown.remainingCount} remaining)`)}`);
-                log(e.err ? e.err.stack : e.message);
-            }
-        });
-        return child;
-    }
-
-    // @ts-ignore
-    start() {
-        throw new Error("Not supported, use fork.");
-    }
-}
-
-class ForkedGulp extends gulp.Gulp {
-    /**
-     * @param {gulp.Gulp["tasks"]} tasks
-     */
-    constructor(tasks) {
-        super();
-        this.tasks = tasks;
-    }
-
-    // Do not reset tasks
-    _resetAllTasks() {}
-    _resetSpecificTasks() {}
-    _resetTask() {}
-}
-
 // internal `Gulp` instance for compilation artifacts.
-const compilationGulp = new CompilationGulp();
+const compilationGulp = new gulp.Gulp();
 
 /** @type {Map<ResolvedProjectSpec, ProjectGraph>} */
 const projectGraphCache = new Map();
@@ -86,6 +29,39 @@ const projectGraphCache = new Map();
 /** @type {Map<string, { typescript: string, alias: string, paths: ResolvedPathOptions }>} */
 const typescriptAliasMap = new Map();
 
+// TODO: allow concurrent outer builds to be run in parallel
+const sem = new Semaphore(1);
+
+/**
+ * @param {string|string[]} taskName 
+ * @param {() => any} [cb] 
+ */
+function start(taskName, cb) {
+    return sem.wait().then(() => new Promise((resolve, reject) => {
+        compilationGulp.start(taskName, err => {
+            if (err) {
+                reject(err);
+            }
+            else if (cb) {
+                try {
+                    resolve(cb());
+                }
+                catch (e) {
+                    reject(err);
+                }
+            }
+            else {
+                resolve();
+            }
+        });
+    })).then(() => {
+        sem.release()
+    }, e => {
+        sem.release();
+        throw e;
+    });
+}
+
 /**
  * Defines a gulp orchestration for a TypeScript project, returning a callback that can be used to trigger compilation.
  * @param {string} projectSpec The path to a tsconfig.json file or its containing directory.
@@ -98,9 +74,7 @@ function createCompiler(projectSpec, options) {
     const projectGraph = getOrCreateProjectGraph(resolvedProjectSpec, resolvedOptions.paths);
     projectGraph.isRoot = true;
     const taskName = compileTaskName(ensureCompileTask(projectGraph, resolvedOptions), resolvedOptions.typescript);
-    return () => new Promise((resolve, reject) => compilationGulp
-        .fork(resolvedOptions.verbose)
-        .start(taskName, err => err ? reject(err) : resolve()));
+    return () => start(taskName);
 }
 exports.createCompiler = createCompiler;
 
@@ -139,9 +113,7 @@ function createCleaner(projectSpec, options) {
     const projectGraph = getOrCreateProjectGraph(resolvedProjectSpec, paths);
     projectGraph.isRoot = true;
     const taskName = cleanTaskName(ensureCleanTask(projectGraph));
-    return () => new Promise((resolve, reject) => compilationGulp
-        .fork()
-        .start(taskName, err => err ? reject(err) : resolve()));
+    return () => start(taskName);
 }
 exports.createCleaner = createCleaner;
 
@@ -811,7 +783,7 @@ function possiblyTriggerRecompilation(config, task) {
 function triggerRecompilation(task, config) {
     compilationGulp._resetTask(task);
     if (config.watchers && config.watchers.size) {
-        compilationGulp.fork().start(task.name, () => {
+        start(task.name, () => {
             /** @type {Set<string>} */
             const taskNames = new Set();
             /** @type {((err?: any) => void)[]} */
@@ -831,7 +803,7 @@ function triggerRecompilation(task, config) {
         });
     }
     else {
-        compilationGulp.fork(/*verbose*/ true).start(task.name);
+        start(task.name);
     }
 }