agentxchain 2.155.51 → 2.155.53

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.155.51",
3
+ "version": "2.155.53",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -523,6 +523,7 @@ function renderPrompt(role, roleId, turn, state, config, root) {
523
523
  if (phaseNames.length > 0) {
524
524
  lines.push(`- \`phase_transition_request\`: set to a **phase name** when gate requirements are met, or \`null\`. Valid phases: ${phaseNames.map((p) => `\`"${p}"\``).join(', ')}`);
525
525
  lines.push('- **Do NOT use exit gate names** (e.g., `planning_signoff`, `implementation_complete`, `qa_ship_verdict`) as `phase_transition_request` values — those are gate identifiers, not phase names');
526
+ lines.push('- **Do NOT skip ahead to later phases**. Use only the immediate next phase named below for the current phase.');
526
527
  } else {
527
528
  lines.push('- `phase_transition_request`: set to next phase name when gate requirements are met, or `null`');
528
529
  }
@@ -4758,7 +4758,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
4758
4758
  // early — the agent didn't do the work required for the transition.
4759
4759
  if (turnResult.phase_transition_request && !isIdleExpansionNewIntakeProposal) {
4760
4760
  const preGateResult = evaluatePhaseExit({
4761
- state,
4761
+ state: { ...state, history: historyEntries },
4762
4762
  config,
4763
4763
  acceptedTurn: turnResult,
4764
4764
  root,
@@ -1390,6 +1390,9 @@ export function normalizeTurnResult(tr, config, context = {}) {
1390
1390
  if (allowedNextRoles.length === 0) return null;
1391
1391
  return allowedNextRoles.find((role) => role !== assignedRole) || allowedNextRoles[0] || null;
1392
1392
  };
1393
+ const nextPhaseEntryRole = nextPhase && routing?.[nextPhase]?.entry_role
1394
+ ? routing[nextPhase].entry_role
1395
+ : null;
1393
1396
 
1394
1397
  // ── Rule 0: infer missing status only when intent is unambiguous ──────
1395
1398
  if (!('status' in normalized)) {
@@ -1537,6 +1540,54 @@ export function normalizeTurnResult(tr, config, context = {}) {
1537
1540
  }
1538
1541
 
1539
1542
  // ── Rule 5: correct invalid or non-forward lifecycle requests ─────────
1543
+ if (
1544
+ isKnownPhase &&
1545
+ !isTerminalPhase &&
1546
+ !isReviewOnly &&
1547
+ normalized.status === 'completed' &&
1548
+ typeof normalized.phase_transition_request === 'string' &&
1549
+ !normalized.run_completion_request
1550
+ ) {
1551
+ const requested = normalized.phase_transition_request;
1552
+ const requestedIndex = phaseNames.indexOf(requested);
1553
+ const skipsImmediateNextPhase = requestedIndex > currentPhaseIndex + 1;
1554
+
1555
+ if (skipsImmediateNextPhase && nextPhase) {
1556
+ normalized.phase_transition_request = nextPhase;
1557
+ corrections.push(
1558
+ `phase_transition_request: corrected skip-forward "${requested}" to immediate next phase "${nextPhase}"`
1559
+ );
1560
+ normalizationEvents.push({
1561
+ field: 'phase_transition_request',
1562
+ original_value: requested,
1563
+ normalized_value: nextPhase,
1564
+ rationale: 'skip_forward_phase_corrected_to_next_phase',
1565
+ });
1566
+
1567
+ const proposedRoleIsStalePhase = normalized.proposed_next_role === requested;
1568
+ const proposedRoleIsRoutingIllegal = typeof normalized.proposed_next_role === 'string'
1569
+ && normalized.proposed_next_role !== 'human'
1570
+ && allowedNextRoles.length > 0
1571
+ && !allowedNextRoles.includes(normalized.proposed_next_role);
1572
+ if (
1573
+ nextPhaseEntryRole &&
1574
+ allowedNextRoles.includes(nextPhaseEntryRole) &&
1575
+ (proposedRoleIsStalePhase || proposedRoleIsRoutingIllegal)
1576
+ ) {
1577
+ corrections.push(
1578
+ `proposed_next_role: corrected "${normalized.proposed_next_role}" to entry role "${nextPhaseEntryRole}" for corrected phase "${nextPhase}"`
1579
+ );
1580
+ normalizationEvents.push({
1581
+ field: 'proposed_next_role',
1582
+ original_value: normalized.proposed_next_role,
1583
+ normalized_value: nextPhaseEntryRole,
1584
+ rationale: 'aligned_to_corrected_phase_entry_role',
1585
+ });
1586
+ normalized.proposed_next_role = nextPhaseEntryRole;
1587
+ }
1588
+ }
1589
+ }
1590
+
1540
1591
  if (
1541
1592
  isKnownPhase &&
1542
1593
  isReviewOnly &&