agentxchain 2.155.21 → 2.155.22
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/package.json +1 -1
- package/src/lib/config.js +5 -1
- package/src/lib/governed-state.js +93 -0
package/package.json
CHANGED
package/src/lib/config.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
reconcileApprovalPausesWithConfig,
|
|
10
10
|
reconcileBudgetStatusWithConfig,
|
|
11
11
|
reconcileRecoveryActionsWithConfig,
|
|
12
|
+
recoverStaleIdleExpansionRun,
|
|
12
13
|
} from './governed-state.js';
|
|
13
14
|
|
|
14
15
|
function attachLegacyCurrentTurnAlias(state) {
|
|
@@ -160,7 +161,10 @@ export function loadProjectState(root, config) {
|
|
|
160
161
|
stateData = reconciledBudget.state;
|
|
161
162
|
const reconciledRecovery = reconcileRecoveryActionsWithConfig(stateData, config);
|
|
162
163
|
stateData = reconciledRecovery.state;
|
|
163
|
-
|
|
164
|
+
// BUG-75: recover stale idle-expansion runs missing charter_materialization_pending
|
|
165
|
+
const staleRecovery = recoverStaleIdleExpansionRun(root, stateData);
|
|
166
|
+
stateData = staleRecovery.state;
|
|
167
|
+
if (normalized.changed || reconciledApprovals.changed || reconciledBudget.changed || reconciledRecovery.changed || staleRecovery.changed) {
|
|
164
168
|
safeWriteJson(filePath, stateData);
|
|
165
169
|
}
|
|
166
170
|
}
|
|
@@ -2500,6 +2500,99 @@ export function normalizeGovernedStateShape(state) {
|
|
|
2500
2500
|
return { state: stripLegacyCurrentTurn(nextState), changed };
|
|
2501
2501
|
}
|
|
2502
2502
|
|
|
2503
|
+
// BUG-75: Recover stale idle-expansion runs created before BUG-74 that lack
|
|
2504
|
+
// charter_materialization_pending. These runs loop on gate_semantic_coverage
|
|
2505
|
+
// because the PM prompt never receives the materialization directive.
|
|
2506
|
+
const INTAKE_EVENTS_DIR = '.agentxchain/intake/events';
|
|
2507
|
+
|
|
2508
|
+
export function recoverStaleIdleExpansionRun(root, state) {
|
|
2509
|
+
if (!state || typeof state !== 'object') {
|
|
2510
|
+
return { state, changed: false };
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
// Only recover active planning runs missing the materialization flag
|
|
2514
|
+
if (
|
|
2515
|
+
state.status !== 'active'
|
|
2516
|
+
|| state.phase !== 'planning'
|
|
2517
|
+
|| state.charter_materialization_pending
|
|
2518
|
+
) {
|
|
2519
|
+
return { state, changed: false };
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
// Trace the run back to its intake intent via provenance
|
|
2523
|
+
const intentId = state.provenance?.intake_intent_id;
|
|
2524
|
+
if (!intentId) {
|
|
2525
|
+
return { state, changed: false };
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
// Read the intent file
|
|
2529
|
+
const intentPath = join(root, INTAKE_INTENTS_DIR, `${intentId}.json`);
|
|
2530
|
+
if (!existsSync(intentPath)) {
|
|
2531
|
+
return { state, changed: false };
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
let intent;
|
|
2535
|
+
try {
|
|
2536
|
+
intent = JSON.parse(readFileSync(intentPath, 'utf8'));
|
|
2537
|
+
} catch {
|
|
2538
|
+
return { state, changed: false };
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
const eventId = intent.event_id;
|
|
2542
|
+
if (!eventId) {
|
|
2543
|
+
return { state, changed: false };
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
// Read the event file to check the category
|
|
2547
|
+
const eventPath = join(root, INTAKE_EVENTS_DIR, `${eventId}.json`);
|
|
2548
|
+
if (!existsSync(eventPath)) {
|
|
2549
|
+
return { state, changed: false };
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
let event;
|
|
2553
|
+
try {
|
|
2554
|
+
event = JSON.parse(readFileSync(eventPath, 'utf8'));
|
|
2555
|
+
} catch {
|
|
2556
|
+
return { state, changed: false };
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
if (event.category !== 'pm_idle_expansion_derived') {
|
|
2560
|
+
return { state, changed: false };
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
// Reconstruct charter_materialization_pending from the intake context
|
|
2564
|
+
const recoveredState = {
|
|
2565
|
+
...state,
|
|
2566
|
+
charter_materialization_pending: {
|
|
2567
|
+
charter: intent.charter || null,
|
|
2568
|
+
acceptance_contract: Array.isArray(intent.acceptance_contract)
|
|
2569
|
+
? intent.acceptance_contract
|
|
2570
|
+
: [],
|
|
2571
|
+
suppressed_transition: 'implementation',
|
|
2572
|
+
source_turn_id: null,
|
|
2573
|
+
recorded_at: new Date().toISOString(),
|
|
2574
|
+
},
|
|
2575
|
+
};
|
|
2576
|
+
|
|
2577
|
+
// Emit recovery event
|
|
2578
|
+
emitRunEvent(root, 'charter_materialization_required', {
|
|
2579
|
+
run_id: state.run_id,
|
|
2580
|
+
phase: state.phase,
|
|
2581
|
+
status: state.status,
|
|
2582
|
+
payload: {
|
|
2583
|
+
suppressed_transition: 'implementation',
|
|
2584
|
+
reason: 'Stale run from idle-expansion-derived intent recovered missing charter_materialization_pending flag.',
|
|
2585
|
+
new_intake_charter: intent.charter || null,
|
|
2586
|
+
source: 'stale_run_recovery',
|
|
2587
|
+
recovered_missing_flag: true,
|
|
2588
|
+
intent_id: intentId,
|
|
2589
|
+
event_id: eventId,
|
|
2590
|
+
},
|
|
2591
|
+
});
|
|
2592
|
+
|
|
2593
|
+
return { state: recoveredState, changed: true };
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2503
2596
|
export function markRunBlocked(root, details) {
|
|
2504
2597
|
const state = readState(root);
|
|
2505
2598
|
if (!state) {
|