Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion .aiox-core/core/orchestration/master-orchestrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ class MasterOrchestrator extends EventEmitter {
this._state = OrchestratorState.INITIALIZED;
this._previousState = null;
this._inFullPipeline = false; // Flag for gate evaluation during full pipeline
this._persistenceAvailable = true;
this._persistenceError = null;

// Execution state
this.executionState = {
Expand Down Expand Up @@ -1084,6 +1086,10 @@ class MasterOrchestrator extends EventEmitter {
async saveState() {
try {
await fs.ensureDir(path.dirname(this.statePath));
const persistedHealth = {
available: true,
lastError: null,
};

// Build comprehensive state object (AC2, AC6, AC7)
const stateToSave = {
Expand Down Expand Up @@ -1128,17 +1134,22 @@ class MasterOrchestrator extends EventEmitter {
// Errors and insights
errors: this.executionState.errors,
insights: this.executionState.insights,
persistence: persistedHealth,

// Session insights
sessionInsights: this._collectSessionInsights(),
};

await fs.writeJson(this.statePath, stateToSave, { spaces: 2 });
this._persistenceAvailable = persistedHealth.available;
this._persistenceError = persistedHealth.lastError;
this._log('State saved successfully', { path: this.statePath });

return true;
} catch (error) {
this._log(`Failed to save state: ${error.message}`, { level: 'warn' });
this._persistenceAvailable = false;
this._persistenceError = error && error.message ? error.message : String(error);
this._log(`Failed to save state: ${this._persistenceError}`, { level: 'warn' });
return false;
}
}
Expand Down Expand Up @@ -1434,6 +1445,10 @@ class MasterOrchestrator extends EventEmitter {
},
errors: this.executionState.errors,
insights: this.executionState.insights,
persistence: {
available: this._persistenceAvailable,
error: this._persistenceError,
},
state: this.executionState,
};
}
Expand Down Expand Up @@ -1475,9 +1490,31 @@ class MasterOrchestrator extends EventEmitter {
]),
),
errors: this.executionState.errors.length,
persistence: {
available: this._persistenceAvailable,
error: this._persistenceError,
},
};
}

/**
* Whether the last state persistence operation succeeded.
*
* @returns {boolean} True when persistence is available.
*/
isPersistenceAvailable() {
return this._persistenceAvailable;
}

/**
* Return the last state persistence error message, if any.
*
* @returns {string|null} Last persistence error message.
*/
getPersistenceError() {
return this._persistenceError;
}

// ═══════════════════════════════════════════════════════════════════════════════════
// LOGGING & CALLBACKS
// ═══════════════════════════════════════════════════════════════════════════════════
Expand Down
6 changes: 3 additions & 3 deletions .aiox-core/data/entity-registry.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
metadata:
version: 1.0.0
lastUpdated: '2026-05-07T21:02:02.654Z'
lastUpdated: '2026-05-08T06:26:24.449Z'
entityCount: 746
checksumAlgorithm: sha256
resolutionRate: 100
Expand Down Expand Up @@ -9745,8 +9745,8 @@ entities:
score: 0.4
constraints: []
extensionPoints: []
checksum: sha256:61c988509c2edc03c1cd4dec333b284f7d15273f3e799c56950706a2e88f341b
lastVerified: '2026-05-07T10:44:44.074Z'
checksum: sha256:88238a9e0ef7d058efe0ca2fe7a9d0e34202bff7e409fd1a97886b0a8ccdce97
lastVerified: '2026-05-08T06:26:24.445Z'
message-formatter:
path: .aiox-core/core/orchestration/message-formatter.js
layer: L1
Expand Down
8 changes: 4 additions & 4 deletions .aiox-core/install-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# - File types for categorization
#
version: 5.1.15
generated_at: "2026-05-08T05:30:43.651Z"
generated_at: "2026-05-08T06:26:53.273Z"
generator: scripts/generate-install-manifest.js
file_count: 1111
files:
Expand Down Expand Up @@ -917,9 +917,9 @@ files:
type: core
size: 9382
- path: core/orchestration/master-orchestrator.js
hash: sha256:29b69047fe780751ae89f6c593cb835254e2e8a536f5581473f474115deec496
hash: sha256:88238a9e0ef7d058efe0ca2fe7a9d0e34202bff7e409fd1a97886b0a8ccdce97
type: core
size: 56868
size: 57977
- path: core/orchestration/message-formatter.js
hash: sha256:b7413c04fa22db1c5fc2f5c2aa47bb8ca0374e079894a44df21b733da6c258ae
type: core
Expand Down Expand Up @@ -1241,7 +1241,7 @@ files:
type: data
size: 9590
- path: data/entity-registry.yaml
hash: sha256:416ffc477206f2b75700a800a35b6af51269f73b3bbc9952a155b1f350089030
hash: sha256:1c8e010516647898377715d160234094bc262b0812cf17c96d7d1d4d36c7b4e8
type: data
size: 522368
- path: data/learned-patterns.yaml
Expand Down
47 changes: 47 additions & 0 deletions tests/core/master-orchestrator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,53 @@ describe('MasterOrchestrator', () => {
await orchestrator.initialize();
const result = await orchestrator.saveState();
expect(result).toBe(true);
expect(orchestrator.isPersistenceAvailable()).toBe(true);
expect(orchestrator.getPersistenceError()).toBeNull();
expect(orchestrator.getStatus().persistence).toEqual({
available: true,
error: null,
});
});

it('should expose persistence degradation when state save fails', async () => {
const writeJsonSpy = jest.spyOn(fs, 'writeJson').mockRejectedValueOnce(new Error('disk full'));

try {
const result = await orchestrator.saveState();

expect(result).toBe(false);
expect(orchestrator.isPersistenceAvailable()).toBe(false);
expect(orchestrator.getPersistenceError()).toBe('disk full');
expect(orchestrator.getStatus().persistence).toEqual({
available: false,
error: 'disk full',
});
expect(orchestrator.finalize().persistence).toEqual({
available: false,
error: 'disk full',
});

writeJsonSpy.mockResolvedValueOnce();
const recoveryResult = await orchestrator.saveState();

expect(recoveryResult).toBe(true);
expect(orchestrator.isPersistenceAvailable()).toBe(true);
expect(orchestrator.getPersistenceError()).toBeNull();
expect(orchestrator.getStatus().persistence).toEqual({
available: true,
error: null,
});
expect(orchestrator.finalize().persistence).toEqual({
available: true,
error: null,
});
expect(writeJsonSpy.mock.calls[1][1].persistence).toEqual({
available: true,
lastError: null,
});
} finally {
writeJsonSpy.mockRestore();
}
});
});

Expand Down
Loading