ultra-builder-pro-cli 0.25.0 → 0.25.1

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/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.25.1] — 2026-07-31
11
+
12
+ ### Fixed
13
+
14
+ - Published state-migration lock ownership atomically from a fully written and
15
+ synchronized candidate inode, so concurrent Node 22 processes cannot mistake a
16
+ valid publication or release window for a malformed lock. Stable malformed locks
17
+ remain fail-closed.
18
+
10
19
  ## [0.25.0] — 2026-07-31
11
20
 
12
21
  ### Added
@@ -599,15 +599,37 @@ function processIsAlive(pid) {
599
599
 
600
600
  function readMigrationGate(gatePath) {
601
601
  const stat = lstatOrNull(gatePath);
602
- if (!stat || stat.isSymbolicLink() || !stat.isFile()) return null;
602
+ if (!stat) return { missing: true };
603
+ if (stat.isSymbolicLink() || !stat.isFile()) return null;
603
604
  try {
604
605
  const owner = JSON.parse(fs.readFileSync(gatePath, 'utf8'));
605
606
  return { owner, stat };
606
- } catch {
607
+ } catch (error) {
608
+ if (error.code === 'ENOENT') return { missing: true };
607
609
  return null;
608
610
  }
609
611
  }
610
612
 
613
+ function createMigrationGateCandidate(gatePath, owner) {
614
+ while (true) {
615
+ const candidatePath = `${gatePath}.candidate-${process.pid}-${randomUUID()}`;
616
+ let descriptor;
617
+ try {
618
+ descriptor = fs.openSync(candidatePath, 'wx', 0o600);
619
+ fs.writeFileSync(descriptor, `${JSON.stringify(owner)}\n`);
620
+ fs.fsyncSync(descriptor);
621
+ return { candidatePath, descriptor };
622
+ } catch (error) {
623
+ if (descriptor !== undefined) {
624
+ try { fs.closeSync(descriptor); } catch { /* best effort */ }
625
+ }
626
+ fs.rmSync(candidatePath, { force: true });
627
+ if (error.code === 'EEXIST') continue;
628
+ throw error;
629
+ }
630
+ }
631
+ }
632
+
611
633
  function reclaimDeadMigrationGate(gatePath, observed) {
612
634
  const quarantine = `${gatePath}.stale-${process.pid}-${randomUUID()}`;
613
635
  fs.renameSync(gatePath, quarantine);
@@ -635,28 +657,28 @@ function acquireStateMigrationGate(paths, {
635
657
  } = {}) {
636
658
  const gatePath = path.join(paths.runtimeDir, 'state-migration.lock');
637
659
  const token = randomUUID();
660
+ const owner = {
661
+ version: 2,
662
+ pid: process.pid,
663
+ owner_started_at: processStartMarker(process.pid),
664
+ token,
665
+ legacy_state_db: paths.legacyStateDbPath,
666
+ runtime_state_db: paths.stateDbPath,
667
+ };
638
668
  const deadline = Date.now() + timeoutMs;
639
669
  let descriptor;
640
670
  while (descriptor === undefined) {
671
+ const candidate = createMigrationGateCandidate(gatePath, owner);
672
+ let published = false;
641
673
  try {
642
- descriptor = fs.openSync(gatePath, 'wx', 0o600);
643
- fs.writeFileSync(descriptor, `${JSON.stringify({
644
- version: 2,
645
- pid: process.pid,
646
- owner_started_at: processStartMarker(process.pid),
647
- token,
648
- legacy_state_db: paths.legacyStateDbPath,
649
- runtime_state_db: paths.stateDbPath,
650
- })}\n`);
651
- fs.fsyncSync(descriptor);
674
+ fs.linkSync(candidate.candidatePath, gatePath);
675
+ descriptor = candidate.descriptor;
676
+ published = true;
652
677
  break;
653
678
  } catch (error) {
654
- if (descriptor !== undefined) {
655
- try { fs.closeSync(descriptor); } catch { /* best effort */ }
656
- descriptor = undefined;
657
- }
658
679
  if (error.code !== 'EEXIST') throw error;
659
680
  const observed = readMigrationGate(gatePath);
681
+ if (observed?.missing) continue;
660
682
  const ownerPid = Number(observed?.owner?.pid);
661
683
  const live = processIsAlive(ownerPid);
662
684
  const currentStart = live ? processStartMarker(ownerPid) : null;
@@ -684,6 +706,11 @@ function acquireStateMigrationGate(paths, {
684
706
  continue;
685
707
  }
686
708
  reclaimDeadMigrationGate(gatePath, observed);
709
+ } finally {
710
+ fs.rmSync(candidate.candidatePath, { force: true });
711
+ if (!published) {
712
+ try { fs.closeSync(candidate.descriptor); } catch { /* best effort */ }
713
+ }
687
714
  }
688
715
  }
689
716
  return () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultra-builder-pro-cli",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Explicitly invoked Ultra Builder Pro workflows for Claude Code, OpenCode, Codex CLI, Kimi Code, and Grok Build, with a project-local MCP persistence and safety kernel.",
5
5
  "bin": {
6
6
  "ultra-builder-pro-cli": "bin/install.js",