hotsheet 0.19.0-beta.1 → 0.19.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +51 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -16431,6 +16431,14 @@ var init_backgroundScheduler = __esm({
16431
16431
  "use strict";
16432
16432
  init_freezeLogger();
16433
16433
  PRIORITY = {
16434
+ /** Startup restoration of the previous session's projects (load-resilience
16435
+ * epic HS-8722, docs/75 — the startup restore path). Highest
16436
+ * priority because each restored project is a user-visible tab — they should
16437
+ * fill in ahead of routine git/markdown/backup churn — but still bounded by
16438
+ * the scheduler's concurrency cap + lag backpressure so the serial fan-out
16439
+ * of N projects can't saturate the event loop on launch (the HS-8721 freeze,
16440
+ * on the one path never migrated onto the scheduler). */
16441
+ PROJECT_RESTORE: 5,
16434
16442
  GIT_STATUS: 10,
16435
16443
  MARKDOWN_SYNC: 20,
16436
16444
  SNAPSHOT: 30,
@@ -20333,7 +20341,10 @@ async function registerProject(dataDir, port) {
20333
20341
  const existing = projects.get(existingSecret);
20334
20342
  if (existing) return existing;
20335
20343
  }
20344
+ const dbT0 = Date.now();
20336
20345
  const db = await getDbForDir(absDataDir);
20346
+ const dbMs = Date.now() - dbT0;
20347
+ if (dbMs > 500) startupLog(`[restore-step] getDbForDir took ${String(dbMs)}ms for ${absDataDir}`);
20337
20348
  const { migrateDbSettingsToFile: migrateDbSettingsToFile2 } = await Promise.resolve().then(() => (init_migrate_settings(), migrate_settings_exports));
20338
20349
  await runWithDataDir(absDataDir, () => migrateDbSettingsToFile2(absDataDir));
20339
20350
  const secret = ensureSecret(absDataDir, port);
@@ -20443,6 +20454,7 @@ var init_projects = __esm({
20443
20454
  init_file_settings();
20444
20455
  init_lock();
20445
20456
  init_skills();
20457
+ init_startup_log();
20446
20458
  init_markdown();
20447
20459
  init_isExecutableOnPath();
20448
20460
  projects = /* @__PURE__ */ new Map();
@@ -39476,8 +39488,11 @@ async function startAndConfigure(port, dataDir, strictPort) {
39476
39488
  }
39477
39489
  async function postStartup(dataDir, actualPort, demo, noOpen) {
39478
39490
  if (demo === null) {
39491
+ startupMark("post-startup: init backup scheduler");
39479
39492
  initBackupScheduler(dataDir);
39493
+ startupMark("post-startup: init snapshot scheduler");
39480
39494
  initSnapshotScheduler(dataDir);
39495
+ startupMark("post-startup: add to project list");
39481
39496
  addToProjectList(dataDir);
39482
39497
  startupMark("post-startup: restoring previous projects");
39483
39498
  await restorePreviousProjects(dataDir, actualPort);
@@ -39500,22 +39515,41 @@ async function postStartup(dataDir, actualPort, demo, noOpen) {
39500
39515
  async function restorePreviousProjects(dataDir, actualPort) {
39501
39516
  const previousProjects = readProjectList();
39502
39517
  const absDataDir = resolve10(dataDir);
39503
- const validProjects = [];
39504
39518
  const { eagerSpawnTerminals: eagerSpawnTerminals2 } = await Promise.resolve().then(() => (init_eagerSpawn(), eagerSpawn_exports));
39505
- for (const prevDir of previousProjects) {
39506
- if (prevDir === absDataDir) {
39507
- validProjects.push(prevDir);
39508
- continue;
39509
- }
39510
- if (!existsSync30(prevDir)) continue;
39511
- try {
39512
- const ctx = await registerProject(prevDir, actualPort);
39513
- validProjects.push(prevDir);
39514
- eagerSpawnTerminals2(ctx.secret, prevDir);
39515
- } catch (e) {
39516
- console.warn(`[startup] Failed to restore project ${prevDir}: ${getErrorMessage(e)}`);
39517
- }
39518
- }
39519
+ const { getBackgroundScheduler: getBackgroundScheduler2, PRIORITY: PRIORITY2 } = await Promise.resolve().then(() => (init_backgroundScheduler(), backgroundScheduler_exports));
39520
+ const { notifyChange: notifyChange2 } = await Promise.resolve().then(() => (init_notify(), notify_exports));
39521
+ const scheduler = getBackgroundScheduler2();
39522
+ const registeredOk = /* @__PURE__ */ new Set();
39523
+ await Promise.all(previousProjects.map((prevDir) => {
39524
+ if (prevDir === absDataDir) return Promise.resolve();
39525
+ if (!existsSync30(prevDir)) return Promise.resolve();
39526
+ return scheduler.submit({
39527
+ key: `project-restore:${prevDir}`,
39528
+ projectKey: prevDir,
39529
+ priority: PRIORITY2.PROJECT_RESTORE,
39530
+ // deferUnderLag MUST be false: restore is user-visible work that has to
39531
+ // make progress. Deferring it under lag starves it — and each restore job
39532
+ // itself spikes lag (PGLite WASM init), so a true `deferUnderLag` makes
39533
+ // the whole restore crawl (observed: 403s for 8 projects).
39534
+ deferUnderLag: false,
39535
+ run: async () => {
39536
+ const t0 = Date.now();
39537
+ startupLog(`[restore-timing] START ${prevDir}`);
39538
+ try {
39539
+ const ctx = await registerProject(prevDir, actualPort);
39540
+ registeredOk.add(prevDir);
39541
+ eagerSpawnTerminals2(ctx.secret, prevDir);
39542
+ notifyChange2();
39543
+ startupLog(`[restore-timing] ${prevDir} registered in ${String(Date.now() - t0)}ms`);
39544
+ } catch (e) {
39545
+ console.warn(`[startup] Failed to restore project ${prevDir}: ${getErrorMessage(e)}`);
39546
+ }
39547
+ }
39548
+ });
39549
+ }));
39550
+ const validProjects = previousProjects.filter(
39551
+ (prevDir) => prevDir === absDataDir || registeredOk.has(prevDir)
39552
+ );
39519
39553
  if (validProjects.length !== previousProjects.length) {
39520
39554
  const { reorderProjectList: reorderProjectList2 } = await Promise.resolve().then(() => (init_project_list(), project_list_exports));
39521
39555
  reorderProjectList2(validProjects);
@@ -39524,7 +39558,6 @@ async function restorePreviousProjects(dataDir, actualPort) {
39524
39558
  const { getProjectByDataDir: getByDir, reorderProjects: reorder } = await Promise.resolve().then(() => (init_projects(), projects_exports));
39525
39559
  const secrets = validProjects.map((dir) => getByDir(dir)?.secret).filter((s) => s !== void 0);
39526
39560
  if (secrets.length > 1) reorder(secrets);
39527
- const { notifyChange: notifyChange2 } = await Promise.resolve().then(() => (init_notify(), notify_exports));
39528
39561
  notifyChange2();
39529
39562
  }
39530
39563
  }
@@ -39757,7 +39790,8 @@ if (isEntryPoint) {
39757
39790
  export {
39758
39791
  computeIsEntryPoint,
39759
39792
  createSignalHandler,
39760
- migrateGlobalConfig
39793
+ migrateGlobalConfig,
39794
+ restorePreviousProjects
39761
39795
  };
39762
39796
  /*! Bundled license information:
39763
39797
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hotsheet",
3
- "version": "0.19.0-beta.1",
3
+ "version": "0.19.0-beta.2",
4
4
  "description": "A lightweight local project management tool. Create, categorize, and prioritize tickets with a fast bullet-list interface, then export an Up Next worklist for AI tools.",
5
5
  "type": "module",
6
6
  "license": "MIT",