ultra-builder-pro-cli 0.20.0 → 0.22.0

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 (109) hide show
  1. package/.ultra-template/docs/research/README.md +26 -5
  2. package/.ultra-template/specs/discovery.md +1 -1
  3. package/.ultra-template/tasks/tasks.json +10 -3
  4. package/.ultra-template/templates/task-context.md +19 -0
  5. package/CHANGELOG.md +106 -0
  6. package/README.md +215 -41
  7. package/adapters/_shared/codex-assets.cjs +69 -11
  8. package/adapters/_shared/interaction-contract.cjs +36 -3
  9. package/adapters/_shared/runtime-assets.cjs +41 -1
  10. package/adapters/claude.js +7 -5
  11. package/adapters/codex.js +1 -1
  12. package/adapters/kimi.js +2 -8
  13. package/adapters/opencode.js +43 -30
  14. package/bin/orchestrator.js +64 -24
  15. package/docs/AGENT-CONTEXT.md +89 -49
  16. package/docs/ARCHITECTURE.md +112 -45
  17. package/docs/ARTIFACT-AUTHORITY.md +274 -0
  18. package/docs/COMMIT-HASH-BACKFILL.md +33 -90
  19. package/docs/DECISIONS.md +14 -6
  20. package/docs/LEGACY-CLI-CROSSWALK.md +16 -6
  21. package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -4
  22. package/docs/ROADMAP.md +20 -6
  23. package/docs/RUNTIME-COMPAT-MATRIX.md +29 -16
  24. package/docs/STATE-DB-ACCESS-POLICY.md +43 -13
  25. package/docs/WORKFLOW-LIFECYCLE.md +105 -50
  26. package/hooks/README.md +10 -8
  27. package/hooks/active_task_context.py +105 -18
  28. package/hooks/context_spine.py +16 -4
  29. package/hooks/health_check.py +5 -4
  30. package/hooks/pre_stop_check.py +5 -2
  31. package/hooks/runtime_paths.py +396 -0
  32. package/hooks/subagent_tracker.py +5 -2
  33. package/hooks/workflow_checkpoint.py +16 -3
  34. package/hooks/workflow_context.py +11 -3
  35. package/hooks/workflow_resume.py +7 -4
  36. package/mcp-server/breadcrumb.cjs +20 -2
  37. package/mcp-server/lib/archive-journal.cjs +1026 -111
  38. package/mcp-server/lib/archive-mutation-worker.py +365 -0
  39. package/mcp-server/lib/artifact-registry.cjs +1127 -0
  40. package/mcp-server/lib/baseline-workflow.cjs +69 -28
  41. package/mcp-server/lib/change-packet.cjs +818 -0
  42. package/mcp-server/lib/change-workflow.cjs +932 -176
  43. package/mcp-server/lib/context-paths.cjs +142 -0
  44. package/mcp-server/lib/context-spine.cjs +511 -45
  45. package/mcp-server/lib/decision-dialogue.cjs +345 -3
  46. package/mcp-server/lib/delivery-transaction.cjs +471 -0
  47. package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
  48. package/mcp-server/lib/doctor.cjs +401 -11
  49. package/mcp-server/lib/git-bootstrap.cjs +458 -47
  50. package/mcp-server/lib/init-project.cjs +240 -95
  51. package/mcp-server/lib/plan-publication-worker.cjs +830 -0
  52. package/mcp-server/lib/plan-store.cjs +700 -5
  53. package/mcp-server/lib/project-breadcrumb.cjs +23 -4
  54. package/mcp-server/lib/projector.cjs +211 -42
  55. package/mcp-server/lib/runtime-paths.cjs +1687 -0
  56. package/mcp-server/lib/safe-project-file.cjs +385 -0
  57. package/mcp-server/lib/spec-learning.cjs +28 -12
  58. package/mcp-server/lib/state-authority.cjs +19 -3
  59. package/mcp-server/lib/state-db.cjs +263 -9
  60. package/mcp-server/lib/state-ops.cjs +254 -50
  61. package/mcp-server/lib/task-ledger.cjs +1342 -0
  62. package/mcp-server/lib/telemetry.cjs +12 -3
  63. package/mcp-server/lib/workflow-state.cjs +610 -58
  64. package/mcp-server/server.cjs +423 -42
  65. package/orchestrator/daemon.cjs +2 -10
  66. package/orchestrator/parallel-orchestrator.cjs +314 -50
  67. package/orchestrator/planner/plan-builder.cjs +1 -1
  68. package/orchestrator/recovery.cjs +125 -7
  69. package/orchestrator/session-close-journal-worker.cjs +5 -0
  70. package/orchestrator/session-close-journal.cjs +1014 -0
  71. package/orchestrator/session-runner.cjs +679 -69
  72. package/orchestrator/worktree-manager.cjs +186 -35
  73. package/package.json +2 -3
  74. package/skills/cc-collab/SKILL.md +4 -1
  75. package/skills/codex-collab/SKILL.md +5 -2
  76. package/skills/ultra-change/SKILL.md +34 -8
  77. package/skills/ultra-deliver/SKILL.md +26 -11
  78. package/skills/ultra-dev/SKILL.md +52 -10
  79. package/skills/ultra-doctor/SKILL.md +16 -3
  80. package/skills/ultra-init/SKILL.md +22 -11
  81. package/skills/ultra-plan/SKILL.md +58 -18
  82. package/skills/ultra-research/SKILL.md +37 -35
  83. package/skills/ultra-review/SKILL.md +14 -8
  84. package/skills/ultra-status/SKILL.md +18 -9
  85. package/skills/ultra-test/SKILL.md +11 -5
  86. package/skills/ultra-think/SKILL.md +6 -2
  87. package/skills/ultra-think/references/decision-dialogue.md +17 -6
  88. package/skills/ultra-verify/SKILL.md +4 -1
  89. package/spec/cli-protocol.md +21 -7
  90. package/spec/mcp-tools.yaml +629 -44
  91. package/spec/schemas/change-delta.v1.schema.json +153 -0
  92. package/spec/schemas/context-file.v4.5.schema.json +1 -1
  93. package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
  94. package/spec/schemas/mcp-tools.schema.json +1 -1
  95. package/spec/schemas/state-db.sql +51 -14
  96. package/spec/schemas/task-ledger.v1.schema.json +217 -0
  97. package/spec/schemas/tasks.v4.5.schema.json +1 -1
  98. package/ultra-tools/cli.cjs +3 -2
  99. package/ultra-tools/commands/db.cjs +76 -25
  100. package/ultra-tools/commands/migrate.cjs +1000 -82
  101. package/ultra-tools/commands/session.cjs +34 -8
  102. package/ultra-tools/commands/status.cjs +32 -6
  103. package/ultra-tools/commands/system.cjs +129 -22
  104. package/.ultra-template/tasks/contexts/TEMPLATE.md +0 -19
  105. package/commands/learn.md +0 -10
  106. package/orchestrator/skill-miner.cjs +0 -133
  107. package/output-styles/coding-vibes.md +0 -67
  108. package/output-styles/structural-thinking.md +0 -61
  109. package/skills/learn/SKILL.md +0 -58
@@ -13,11 +13,14 @@
13
13
 
14
14
  const fs = require('node:fs');
15
15
  const path = require('node:path');
16
+ const Database = require('better-sqlite3');
16
17
 
17
18
  const { EXPECTED_VERSION, initStateDb } = require('./state-db.cjs');
18
19
  const { assertStateAuthority, inspectProjection } = require('./state-authority.cjs');
20
+ const taskLedger = require('./task-ledger.cjs');
19
21
  const baselines = require('./baseline-workflow.cjs');
20
22
  const gitBootstrap = require('./git-bootstrap.cjs');
23
+ const runtimePaths = require('./runtime-paths.cjs');
21
24
  const runtimeState = require('./runtime-state.cjs');
22
25
  const ops = require('./state-ops.cjs');
23
26
  const workflows = require('./workflow-state.cjs');
@@ -407,16 +410,23 @@ function rollbackInitialization(ultraDir, backupPath) {
407
410
  return errors;
408
411
  }
409
412
 
410
- function createResumeSnapshot(db, ultraDir, stateDbPath) {
413
+ function createResumeSnapshot(ultraDir, stateDbPath) {
411
414
  const token = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
412
- const statePath = path.join(ultraDir, `.resume-state-${token}.db`);
413
- const projectionDir = path.join(ultraDir, `.resume-projections-${token}`);
415
+ const runtimeDir = path.dirname(stateDbPath);
416
+ const statePath = path.join(runtimeDir, `.resume-state-${token}.db`);
417
+ const projectionDir = path.join(runtimeDir, `.resume-projections-${token}`);
414
418
  const projectionTargets = [
415
419
  path.join('tasks', 'tasks.json'),
416
420
  path.join('tasks', 'contexts'),
421
+ path.join('.runtime', 'projections'),
422
+ path.join('.runtime', 'backups', 'task-ledger'),
417
423
  ];
424
+ let db;
418
425
  try {
426
+ db = new Database(stateDbPath, { fileMustExist: true });
419
427
  db.prepare('VACUUM INTO ?').run(statePath);
428
+ db.close();
429
+ db = null;
420
430
  const projections = projectionTargets.map((relative) => {
421
431
  const source = path.join(ultraDir, relative);
422
432
  const existed = fs.existsSync(source);
@@ -429,6 +439,7 @@ function createResumeSnapshot(db, ultraDir, stateDbPath) {
429
439
  });
430
440
  return { statePath, projectionDir, projections, stateDbPath, ultraDir };
431
441
  } catch (error) {
442
+ if (db?.open) db.close();
432
443
  for (const target of [statePath, projectionDir]) {
433
444
  try { fs.rmSync(target, { recursive: true, force: true }); } catch { /* best effort */ }
434
445
  }
@@ -446,23 +457,55 @@ function discardResumeSnapshot(snapshot) {
446
457
  return errors;
447
458
  }
448
459
 
449
- function restoreResumeSnapshot(snapshot, { stateSourcePath = null } = {}) {
460
+ function restoreResumeSnapshot(snapshot, {
461
+ renameSync = fs.renameSync,
462
+ } = {}) {
450
463
  if (!snapshot) return [];
451
464
  const errors = [];
452
- const source = stateSourcePath && fs.existsSync(stateSourcePath)
453
- ? stateSourcePath
454
- : snapshot.statePath;
465
+ const runtimeDir = path.dirname(snapshot.stateDbPath);
466
+ const liveStage = path.join(
467
+ runtimeDir,
468
+ `.resume-live-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,
469
+ );
470
+ const movedLive = [];
455
471
  try {
472
+ fs.mkdirSync(liveStage, { recursive: false });
456
473
  for (const suffix of ['', '-wal', '-shm']) {
457
- fs.rmSync(`${snapshot.stateDbPath}${suffix}`, { force: true });
458
- }
459
- if (source === snapshot.statePath) {
460
- fs.renameSync(source, snapshot.stateDbPath);
461
- } else {
462
- fs.copyFileSync(source, snapshot.stateDbPath);
474
+ const live = `${snapshot.stateDbPath}${suffix}`;
475
+ if (!fs.existsSync(live)) continue;
476
+ const staged = path.join(liveStage, `state.db${suffix}`);
477
+ renameSync(live, staged);
478
+ movedLive.push({ live, staged });
463
479
  }
480
+ renameSync(snapshot.statePath, snapshot.stateDbPath);
464
481
  } catch (error) {
465
482
  errors.push(`state.db restore failed: ${error.message}`);
483
+ for (const item of [...movedLive].reverse()) {
484
+ try {
485
+ if (fs.existsSync(item.live)) {
486
+ errors.push(`live state rollback target already exists: ${item.live}`);
487
+ continue;
488
+ }
489
+ renameSync(item.staged, item.live);
490
+ } catch (rollbackError) {
491
+ errors.push(`live state rollback failed for ${item.live}: ${rollbackError.message}`);
492
+ }
493
+ }
494
+ // The snapshot and staged live image are recovery evidence. Never destroy
495
+ // either when publication or rollback is incomplete.
496
+ try {
497
+ if (fs.existsSync(liveStage) && fs.readdirSync(liveStage).length === 0) {
498
+ fs.rmdirSync(liveStage);
499
+ }
500
+ } catch (cleanupError) {
501
+ errors.push(`live state staging cleanup failed: ${cleanupError.message}`);
502
+ }
503
+ return errors;
504
+ }
505
+ try {
506
+ fs.rmSync(liveStage, { recursive: true, force: true });
507
+ } catch (error) {
508
+ errors.push(`prior live state cleanup failed: ${error.message}`);
466
509
  }
467
510
  for (const item of snapshot.projections) {
468
511
  const target = path.join(snapshot.ultraDir, item.relative);
@@ -477,7 +520,9 @@ function restoreResumeSnapshot(snapshot, { stateSourcePath = null } = {}) {
477
520
  errors.push(`projection restore failed for ${item.relative}: ${error.message}`);
478
521
  }
479
522
  }
480
- errors.push(...discardResumeSnapshot(snapshot));
523
+ if (errors.length === 0) {
524
+ errors.push(...discardResumeSnapshot(snapshot));
525
+ }
481
526
  return errors;
482
527
  }
483
528
 
@@ -523,7 +568,7 @@ function ensureInitializationWorkflows(db, baseline, { rootDir }) {
523
568
  status: 'completed',
524
569
  evidence: [{
525
570
  kind: 'authority',
526
- ref: '.ultra/state.db',
571
+ ref: '.ultra/.runtime/state.db',
527
572
  summary: 'State authority, scaffold, projection, and repository classification were verified.',
528
573
  }],
529
574
  }, { rootDir });
@@ -561,94 +606,158 @@ function resumeProject({
561
606
  absTarget, ultraDir, source, project_name, project_type, stack, resolvedMode,
562
607
  scope, repositoryProfile, projection, git_mode,
563
608
  }) {
564
- const stateDbPath = path.join(ultraDir, 'state.db');
565
- const stateExisted = fs.existsSync(stateDbPath);
609
+ let locatedStatePath;
610
+ try {
611
+ locatedStatePath = runtimePaths.locateStateDb(absTarget, { env: {} });
612
+ } catch (error) {
613
+ throw new InitProjectError(
614
+ error.code || 'RUNTIME_STATE_CONFLICT',
615
+ error.message,
616
+ { cause: error, details: error.details },
617
+ );
618
+ }
619
+ const stateExisted = fs.existsSync(locatedStatePath);
620
+ let runtimeLayout;
621
+ let gitSetup;
622
+ const stateDbPath = runtimePaths.pathsFor(absTarget).stateDbPath;
566
623
  let initialized;
567
624
  let copiedFiles = [];
568
625
  let baselineResult;
569
626
  let workflowResult;
570
- let gitSetup;
627
+ let teamCheckpoint;
571
628
  let resumeSnapshot;
572
- try {
573
- initialized = initStateDb(stateDbPath);
574
- const { db } = initialized;
575
- if (stateExisted) {
576
- resumeSnapshot = createResumeSnapshot(db, ultraDir, stateDbPath);
577
- }
578
- if (projection && projection.taskCount > 0) assertStateAuthority(db, absTarget);
579
- copiedFiles = copyMissingTemplate(source, ultraDir);
580
- validateTasksTemplate(ultraDir);
581
- gitSetup = gitBootstrap.bootstrapGit(absTarget, { mode: git_mode });
582
- repositoryProfile.git = gitSetup.result;
583
- baselineResult = baselines.readBaseline(db);
584
- if (!baselineResult) {
585
- baselineResult = baselines.startBaseline(db, {
586
- id: 'project-baseline', project_name, project_type, stack,
587
- mode: resolvedMode, repository_revision: baselines.gitHead(absTarget),
588
- scope: scope === undefined ? ['.'] : scope,
589
- classification: repositoryProfile,
590
- }, { rootDir: absTarget });
591
- } else {
592
- baselineResult = baselines.refreshInProgressBaseline(db, {
593
- id: baselineResult.id,
594
- project_name,
595
- project_type,
596
- stack,
597
- classification: repositoryProfile,
598
- }, { rootDir: absTarget });
599
- }
600
- workflowResult = ensureInitializationWorkflows(db, baselineResult, { rootDir: absTarget });
601
- ops.appendEvent(db, {
602
- type: 'project_resumed',
603
- payload: {
604
- project_name, mode: baselineResult.mode, baseline_id: baselineResult.id,
605
- installed_missing_files: copiedFiles.length, git: gitSetup.result,
606
- },
607
- });
608
- const job = runtimeState.enqueueProjection(db, { tool_name: 'task.init_project' });
609
- const projectionResult = runtimeState.processProjectionJobs(db, { rootDir: absTarget, limit: 1 });
610
- const projected = projectionResult.jobs.find((item) => item.id === job.id);
611
- if (!projected || projected.status !== 'completed') {
612
- throw new Error(
613
- `resume projection did not complete: ${projected?.error || 'projection job missing'}`,
629
+ const completeResume = () => {
630
+ try {
631
+ if (stateExisted) {
632
+ // Snapshot the exact entry authority before initStateDb can migrate its
633
+ // schema. A failed snapshot therefore cannot partially upgrade state.
634
+ resumeSnapshot = createResumeSnapshot(ultraDir, stateDbPath);
635
+ }
636
+ initialized = initStateDb(stateDbPath);
637
+ const { db } = initialized;
638
+ if (projection && (
639
+ projection.taskCount > 0 || projection.kind === taskLedger.LEDGER_KIND
640
+ )) {
641
+ teamCheckpoint = assertStateAuthority(db, absTarget, { importTeamLedger: true });
642
+ }
643
+ copiedFiles = copyMissingTemplate(source, ultraDir);
644
+ validateTasksTemplate(ultraDir);
645
+ repositoryProfile.git = gitSetup.result;
646
+ baselineResult = baselines.readBaseline(db);
647
+ if (!baselineResult) {
648
+ baselineResult = baselines.startBaseline(db, {
649
+ id: 'project-baseline', project_name, project_type, stack,
650
+ mode: resolvedMode, repository_revision: baselines.gitHead(absTarget),
651
+ scope: scope === undefined ? ['.'] : scope,
652
+ classification: repositoryProfile,
653
+ }, { rootDir: absTarget });
654
+ } else {
655
+ baselineResult = baselines.refreshInProgressBaseline(db, {
656
+ id: baselineResult.id,
657
+ project_name,
658
+ project_type,
659
+ stack,
660
+ classification: repositoryProfile,
661
+ }, { rootDir: absTarget });
662
+ }
663
+ workflowResult = ensureInitializationWorkflows(db, baselineResult, { rootDir: absTarget });
664
+ if (projection?.kind === 'legacy-task-projection') {
665
+ const publication = taskLedger.publishTaskLedger(db, {
666
+ rootDir: absTarget,
667
+ reason: 'legacy_projection_upgraded',
668
+ });
669
+ teamCheckpoint = {
670
+ generation: publication.ledger.generation,
671
+ state_digest: publication.ledger.state_digest,
672
+ imported: 0,
673
+ deleted: 0,
674
+ imported_changes: 0,
675
+ imported_baseline: false,
676
+ requires_plan_revalidation: false,
677
+ requires_baseline_revalidation: false,
678
+ already_current: true,
679
+ published: publication.changed,
680
+ migrated_legacy_projection: Boolean(publication.migrated_legacy_projection),
681
+ legacy_backup_path: publication.legacy_backup_path || null,
682
+ };
683
+ }
684
+ ops.appendEvent(db, {
685
+ type: 'project_resumed',
686
+ payload: {
687
+ project_name, mode: baselineResult.mode, baseline_id: baselineResult.id,
688
+ installed_missing_files: copiedFiles.length, git: gitSetup.result,
689
+ },
690
+ });
691
+ const job = runtimeState.enqueueProjection(db, { tool_name: 'task.init_project' });
692
+ const projectionResult = runtimeState.processProjectionJobs(
693
+ db, { rootDir: absTarget, limit: 1 },
614
694
  );
615
- }
616
- } catch (error) {
617
- if (initialized?.db?.open) initialized.db.close();
618
- const stateRollbackErrors = stateExisted
619
- ? restoreResumeSnapshot(resumeSnapshot, {
620
- // initStateDb creates this durable snapshot before a schema migration.
621
- // A later resume failure must return to the exact entry authority, not
622
- // merely to the successfully migrated intermediate database.
623
- stateSourcePath: initialized?.backup_path || null,
624
- })
625
- : [];
626
- const gitRollbackErrors = gitBootstrap.rollbackGitBootstrap(absTarget, gitSetup?.internal);
627
- for (const rel of copiedFiles.reverse()) {
628
- try { fs.rmSync(path.join(ultraDir, rel), { force: true }); } catch { /* best effort */ }
629
- }
630
- if (!stateExisted) {
631
- for (const suffix of ['', '-wal', '-shm']) {
632
- try { fs.rmSync(`${stateDbPath}${suffix}`, { force: true }); } catch { /* best effort */ }
695
+ const projected = projectionResult.jobs.find((item) => item.id === job.id);
696
+ if (!projected || projected.status !== 'completed') {
697
+ throw new Error(
698
+ `resume projection did not complete: ${projected?.error || 'projection job missing'}`,
699
+ );
700
+ }
701
+ } catch (error) {
702
+ if (initialized?.db?.open) initialized.db.close();
703
+ const stateRollbackErrors = stateExisted
704
+ ? restoreResumeSnapshot(resumeSnapshot)
705
+ : [];
706
+ const gitRollbackErrors = gitBootstrap.rollbackGitBootstrap(
707
+ absTarget, gitSetup?.internal,
708
+ );
709
+ for (const rel of copiedFiles.reverse()) {
710
+ try { fs.rmSync(path.join(ultraDir, rel), { force: true }); } catch { /* best effort */ }
711
+ }
712
+ if (!stateExisted) {
713
+ for (const suffix of ['', '-wal', '-shm']) {
714
+ try { fs.rmSync(`${stateDbPath}${suffix}`, { force: true }); } catch { /* best effort */ }
715
+ }
716
+ }
717
+ const rollbackErrors = [...stateRollbackErrors, ...gitRollbackErrors];
718
+ if (error instanceof taskLedger.TaskLedgerError
719
+ || (error.code && [
720
+ 'LEGACY_STATE_MIGRATION_REQUIRED',
721
+ 'STATE_AUTHORITY_CONFLICT',
722
+ 'STATE_PROJECTION_INVALID',
723
+ ].includes(error.code))) {
724
+ throw new InitProjectError(
725
+ error.code,
726
+ error.message,
727
+ { cause: error, details: error.details },
728
+ );
729
+ }
730
+ if (error instanceof gitBootstrap.GitBootstrapError) {
731
+ throw new InitProjectError(
732
+ error.code,
733
+ `${error.message}${rollbackErrors.length ? `; ${rollbackErrors.join('; ')}` : ''}`,
734
+ { cause: error },
735
+ );
633
736
  }
634
- }
635
- const rollbackErrors = [...stateRollbackErrors, ...gitRollbackErrors];
636
- if (error.code && ['LEGACY_STATE_MIGRATION_REQUIRED', 'STATE_AUTHORITY_CONFLICT', 'STATE_PROJECTION_INVALID'].includes(error.code)) {
637
- throw new InitProjectError(error.code, error.message, { cause: error, details: error.details });
638
- }
639
- if (error instanceof gitBootstrap.GitBootstrapError) {
640
737
  throw new InitProjectError(
641
- error.code,
642
- `${error.message}${rollbackErrors.length ? `; ${rollbackErrors.join('; ')}` : ''}`,
643
- { cause: error },
738
+ 'IO_ERROR',
739
+ `project resume failed: ${error.message}; `
740
+ + `${rollbackErrors.length ? 'resume rollback was incomplete' : 'existing authority and projections were restored'}`
741
+ + `${rollbackErrors.length ? `; ${rollbackErrors.join('; ')}` : ''}`,
742
+ { retriable: true, cause: error },
644
743
  );
645
744
  }
745
+ };
746
+
747
+ try {
748
+ runtimeLayout = runtimePaths.ensureRuntimeState(absTarget, {
749
+ admitStorageBoundary: () => {
750
+ gitSetup = gitBootstrap.bootstrapGit(absTarget, { mode: git_mode });
751
+ completeResume();
752
+ return gitSetup;
753
+ },
754
+ });
755
+ } catch (error) {
756
+ if (error instanceof InitProjectError) throw error;
646
757
  throw new InitProjectError(
647
- 'IO_ERROR',
648
- `project resume failed: ${error.message}; `
649
- + `${rollbackErrors.length ? 'resume rollback was incomplete' : 'existing authority and projections were restored'}`
650
- + `${rollbackErrors.length ? `; ${rollbackErrors.join('; ')}` : ''}`,
651
- { retriable: true, cause: error },
758
+ error.code || 'RUNTIME_STATE_MIGRATION_FAILED',
759
+ error.message,
760
+ { cause: error, details: error.details },
652
761
  );
653
762
  }
654
763
  initialized.db.close();
@@ -675,7 +784,26 @@ function resumeProject({
675
784
  git: gitSetup.result,
676
785
  workflow: workflowResult,
677
786
  };
787
+ if (teamCheckpoint) {
788
+ result.team_checkpoint = {
789
+ generation: teamCheckpoint.generation,
790
+ state_digest: teamCheckpoint.state_digest,
791
+ imported_tasks: teamCheckpoint.imported,
792
+ deleted_tasks: teamCheckpoint.deleted,
793
+ imported_changes: teamCheckpoint.imported_changes,
794
+ imported_baseline: teamCheckpoint.imported_baseline,
795
+ requires_plan_revalidation: teamCheckpoint.requires_plan_revalidation,
796
+ requires_baseline_revalidation: teamCheckpoint.requires_baseline_revalidation,
797
+ already_current: teamCheckpoint.already_current,
798
+ published: Boolean(teamCheckpoint.published),
799
+ migrated_legacy_projection: Boolean(teamCheckpoint.migrated_legacy_projection),
800
+ legacy_backup_path: teamCheckpoint.legacy_backup_path || undefined,
801
+ };
802
+ }
678
803
  if (initialized.backup_path) result.migration_backup_path = initialized.backup_path;
804
+ if (runtimeLayout.backupPath) {
805
+ result.runtime_migration_backup_path = runtimeLayout.backupPath;
806
+ }
679
807
  return result;
680
808
  }
681
809
 
@@ -725,8 +853,19 @@ function initProject({
725
853
  cause: error, details: error.details,
726
854
  });
727
855
  }
856
+ let locatedStatePath;
857
+ try {
858
+ locatedStatePath = runtimePaths.locateStateDb(absTarget, { env: {} });
859
+ } catch (error) {
860
+ throw new InitProjectError(
861
+ error.code || 'RUNTIME_STATE_CONFLICT',
862
+ error.message,
863
+ { cause: error, details: error.details },
864
+ );
865
+ }
728
866
  if (projection && projection.taskCount > 0
729
- && !fs.existsSync(path.join(ultraDir, 'state.db'))) {
867
+ && projection.kind !== taskLedger.LEDGER_KIND
868
+ && !fs.existsSync(locatedStatePath)) {
730
869
  throw projectionMigrationError(projection);
731
870
  }
732
871
  return resumeProject({
@@ -758,7 +897,8 @@ function initProject({
758
897
  validateTasksTemplate(ultraDir);
759
898
  gitSetup = gitBootstrap.bootstrapGit(absTarget, { mode: git_mode });
760
899
  repositoryProfile.git = gitSetup.result;
761
- const stateDbPath = path.join(ultraDir, 'state.db');
900
+ const runtimeLayout = runtimePaths.ensureRuntimeState(absTarget);
901
+ const stateDbPath = runtimeLayout.stateDbPath;
762
902
  const { db } = initStateDb(stateDbPath);
763
903
  try {
764
904
  baselineResult = baselines.startBaseline(db, {
@@ -805,7 +945,7 @@ function initProject({
805
945
  );
806
946
  }
807
947
 
808
- const stateDbPath = path.join(ultraDir, 'state.db');
948
+ const stateDbPath = runtimePaths.pathsFor(absTarget).stateDbPath;
809
949
  const result = {
810
950
  created_path: ultraDir,
811
951
  state_db_path: stateDbPath,
@@ -834,4 +974,9 @@ function initProject({
834
974
 
835
975
  module.exports = {
836
976
  initProject, InitProjectError, DEFAULT_TEMPLATE, detectProjectMode, classifyRepository,
977
+ _internal: {
978
+ createResumeSnapshot,
979
+ discardResumeSnapshot,
980
+ restoreResumeSnapshot,
981
+ },
837
982
  };