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.
- package/.ultra-template/docs/research/README.md +26 -5
- package/.ultra-template/specs/discovery.md +1 -1
- package/.ultra-template/tasks/tasks.json +10 -3
- package/.ultra-template/templates/task-context.md +19 -0
- package/CHANGELOG.md +106 -0
- package/README.md +215 -41
- package/adapters/_shared/codex-assets.cjs +69 -11
- package/adapters/_shared/interaction-contract.cjs +36 -3
- package/adapters/_shared/runtime-assets.cjs +41 -1
- package/adapters/claude.js +7 -5
- package/adapters/codex.js +1 -1
- package/adapters/kimi.js +2 -8
- package/adapters/opencode.js +43 -30
- package/bin/orchestrator.js +64 -24
- package/docs/AGENT-CONTEXT.md +89 -49
- package/docs/ARCHITECTURE.md +112 -45
- package/docs/ARTIFACT-AUTHORITY.md +274 -0
- package/docs/COMMIT-HASH-BACKFILL.md +33 -90
- package/docs/DECISIONS.md +14 -6
- package/docs/LEGACY-CLI-CROSSWALK.md +16 -6
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -4
- package/docs/ROADMAP.md +20 -6
- package/docs/RUNTIME-COMPAT-MATRIX.md +29 -16
- package/docs/STATE-DB-ACCESS-POLICY.md +43 -13
- package/docs/WORKFLOW-LIFECYCLE.md +105 -50
- package/hooks/README.md +10 -8
- package/hooks/active_task_context.py +105 -18
- package/hooks/context_spine.py +16 -4
- package/hooks/health_check.py +5 -4
- package/hooks/pre_stop_check.py +5 -2
- package/hooks/runtime_paths.py +396 -0
- package/hooks/subagent_tracker.py +5 -2
- package/hooks/workflow_checkpoint.py +16 -3
- package/hooks/workflow_context.py +11 -3
- package/hooks/workflow_resume.py +7 -4
- package/mcp-server/breadcrumb.cjs +20 -2
- package/mcp-server/lib/archive-journal.cjs +1026 -111
- package/mcp-server/lib/archive-mutation-worker.py +365 -0
- package/mcp-server/lib/artifact-registry.cjs +1127 -0
- package/mcp-server/lib/baseline-workflow.cjs +69 -28
- package/mcp-server/lib/change-packet.cjs +818 -0
- package/mcp-server/lib/change-workflow.cjs +932 -176
- package/mcp-server/lib/context-paths.cjs +142 -0
- package/mcp-server/lib/context-spine.cjs +511 -45
- package/mcp-server/lib/decision-dialogue.cjs +345 -3
- package/mcp-server/lib/delivery-transaction.cjs +471 -0
- package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
- package/mcp-server/lib/doctor.cjs +401 -11
- package/mcp-server/lib/git-bootstrap.cjs +458 -47
- package/mcp-server/lib/init-project.cjs +240 -95
- package/mcp-server/lib/plan-publication-worker.cjs +830 -0
- package/mcp-server/lib/plan-store.cjs +700 -5
- package/mcp-server/lib/project-breadcrumb.cjs +23 -4
- package/mcp-server/lib/projector.cjs +211 -42
- package/mcp-server/lib/runtime-paths.cjs +1687 -0
- package/mcp-server/lib/safe-project-file.cjs +385 -0
- package/mcp-server/lib/spec-learning.cjs +28 -12
- package/mcp-server/lib/state-authority.cjs +19 -3
- package/mcp-server/lib/state-db.cjs +263 -9
- package/mcp-server/lib/state-ops.cjs +254 -50
- package/mcp-server/lib/task-ledger.cjs +1342 -0
- package/mcp-server/lib/telemetry.cjs +12 -3
- package/mcp-server/lib/workflow-state.cjs +610 -58
- package/mcp-server/server.cjs +423 -42
- package/orchestrator/daemon.cjs +2 -10
- package/orchestrator/parallel-orchestrator.cjs +314 -50
- package/orchestrator/planner/plan-builder.cjs +1 -1
- package/orchestrator/recovery.cjs +125 -7
- package/orchestrator/session-close-journal-worker.cjs +5 -0
- package/orchestrator/session-close-journal.cjs +1014 -0
- package/orchestrator/session-runner.cjs +679 -69
- package/orchestrator/worktree-manager.cjs +186 -35
- package/package.json +2 -3
- package/skills/cc-collab/SKILL.md +4 -1
- package/skills/codex-collab/SKILL.md +5 -2
- package/skills/ultra-change/SKILL.md +34 -8
- package/skills/ultra-deliver/SKILL.md +26 -11
- package/skills/ultra-dev/SKILL.md +52 -10
- package/skills/ultra-doctor/SKILL.md +16 -3
- package/skills/ultra-init/SKILL.md +22 -11
- package/skills/ultra-plan/SKILL.md +58 -18
- package/skills/ultra-research/SKILL.md +37 -35
- package/skills/ultra-review/SKILL.md +14 -8
- package/skills/ultra-status/SKILL.md +18 -9
- package/skills/ultra-test/SKILL.md +11 -5
- package/skills/ultra-think/SKILL.md +6 -2
- package/skills/ultra-think/references/decision-dialogue.md +17 -6
- package/skills/ultra-verify/SKILL.md +4 -1
- package/spec/cli-protocol.md +21 -7
- package/spec/mcp-tools.yaml +629 -44
- package/spec/schemas/change-delta.v1.schema.json +153 -0
- package/spec/schemas/context-file.v4.5.schema.json +1 -1
- package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +51 -14
- package/spec/schemas/task-ledger.v1.schema.json +217 -0
- package/spec/schemas/tasks.v4.5.schema.json +1 -1
- package/ultra-tools/cli.cjs +3 -2
- package/ultra-tools/commands/db.cjs +76 -25
- package/ultra-tools/commands/migrate.cjs +1000 -82
- package/ultra-tools/commands/session.cjs +34 -8
- package/ultra-tools/commands/status.cjs +32 -6
- package/ultra-tools/commands/system.cjs +129 -22
- package/.ultra-template/tasks/contexts/TEMPLATE.md +0 -19
- package/commands/learn.md +0 -10
- package/orchestrator/skill-miner.cjs +0 -133
- package/output-styles/coding-vibes.md +0 -67
- package/output-styles/structural-thinking.md +0 -61
- 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(
|
|
413
|
+
function createResumeSnapshot(ultraDir, stateDbPath) {
|
|
411
414
|
const token = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
412
|
-
const
|
|
413
|
-
const
|
|
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, {
|
|
460
|
+
function restoreResumeSnapshot(snapshot, {
|
|
461
|
+
renameSync = fs.renameSync,
|
|
462
|
+
} = {}) {
|
|
450
463
|
if (!snapshot) return [];
|
|
451
464
|
const errors = [];
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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.
|
|
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
|
-
|
|
565
|
-
|
|
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
|
|
627
|
+
let teamCheckpoint;
|
|
571
628
|
let resumeSnapshot;
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
-
|
|
642
|
-
|
|
643
|
-
|
|
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
|
-
'
|
|
648
|
-
|
|
649
|
-
|
|
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
|
-
&&
|
|
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
|
|
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 =
|
|
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
|
};
|