agentxchain 2.155.20 → 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 +124 -0
- package/src/lib/intake.js +1 -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) {
|
|
@@ -3312,8 +3405,39 @@ export function initializeGovernedRun(root, config, options = {}) {
|
|
|
3312
3405
|
}
|
|
3313
3406
|
}
|
|
3314
3407
|
|
|
3408
|
+
// BUG-74: When a new run starts from an idle-expansion-derived intent,
|
|
3409
|
+
// set charter_materialization_pending so the first PM turn receives the
|
|
3410
|
+
// "You MUST create or update these planning artifacts" directive.
|
|
3411
|
+
const intakeCtx = options.intakeContext;
|
|
3412
|
+
if (intakeCtx?.category === 'pm_idle_expansion_derived') {
|
|
3413
|
+
updatedState.charter_materialization_pending = {
|
|
3414
|
+
charter: intakeCtx.charter || null,
|
|
3415
|
+
acceptance_contract: Array.isArray(intakeCtx.acceptance_contract)
|
|
3416
|
+
? intakeCtx.acceptance_contract
|
|
3417
|
+
: [],
|
|
3418
|
+
suppressed_transition: 'implementation',
|
|
3419
|
+
source_turn_id: null,
|
|
3420
|
+
recorded_at: new Date().toISOString(),
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3315
3424
|
writeState(root, updatedState);
|
|
3316
3425
|
|
|
3426
|
+
// BUG-74: emit event so consumers know materialization is required from run start
|
|
3427
|
+
if (updatedState.charter_materialization_pending) {
|
|
3428
|
+
emitRunEvent(root, 'charter_materialization_required', {
|
|
3429
|
+
run_id: runId,
|
|
3430
|
+
phase: updatedState.phase,
|
|
3431
|
+
status: 'active',
|
|
3432
|
+
payload: {
|
|
3433
|
+
suppressed_transition: 'implementation',
|
|
3434
|
+
reason: 'New run from idle-expansion-derived intent must materialize charter into planning artifacts.',
|
|
3435
|
+
new_intake_charter: updatedState.charter_materialization_pending.charter,
|
|
3436
|
+
source: 'run_initialization',
|
|
3437
|
+
},
|
|
3438
|
+
});
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3317
3441
|
const startupIntents = archiveStaleIntentsForRun(root, runId, {
|
|
3318
3442
|
protocolVersion: updatedState.protocol_version || '2.x',
|
|
3319
3443
|
});
|
package/src/lib/intake.js
CHANGED
|
@@ -1139,6 +1139,7 @@ export function startIntent(root, intentId, options = {}) {
|
|
|
1139
1139
|
const initResult = initializeGovernedRun(root, config, {
|
|
1140
1140
|
provenance: startProvenance,
|
|
1141
1141
|
allow_terminal_restart: allowCompletedRestart,
|
|
1142
|
+
intakeContext,
|
|
1142
1143
|
});
|
|
1143
1144
|
if (!initResult.ok) {
|
|
1144
1145
|
return { ok: false, error: `run initialization failed: ${initResult.error}`, exitCode: 1 };
|