agentxchain 2.155.12 → 2.155.14

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.12",
3
+ "version": "2.155.14",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -302,6 +302,36 @@ function renderPrompt(role, roleId, turn, state, config, root) {
302
302
  lines.push('');
303
303
  }
304
304
 
305
+ // BUG-70: Charter materialization directive for PM after idle-expansion new_intake_intent
306
+ if (state.charter_materialization_pending && phase === 'planning') {
307
+ const cmp = state.charter_materialization_pending;
308
+ lines.push('### Charter Materialization Required');
309
+ lines.push('');
310
+ lines.push('A previous idle-expansion turn proposed a new intake intent that has NOT been materialized into planning artifacts.');
311
+ lines.push('The phase transition to implementation was suppressed because the planning artifacts do not yet charter the new work.');
312
+ lines.push('');
313
+ lines.push('**Your job this turn is to materialize this charter into concrete planning artifacts:**');
314
+ lines.push('');
315
+ if (cmp.charter) {
316
+ lines.push(`- **Charter:** ${cmp.charter}`);
317
+ }
318
+ if (Array.isArray(cmp.acceptance_contract) && cmp.acceptance_contract.length > 0) {
319
+ lines.push('- **Acceptance Contract:**');
320
+ for (const req of cmp.acceptance_contract) {
321
+ lines.push(` - ${req}`);
322
+ }
323
+ }
324
+ lines.push('');
325
+ lines.push('You MUST create or update these planning artifacts to reflect the new work:');
326
+ lines.push('- `.planning/SYSTEM_SPEC.md` — technical specification for the new increment');
327
+ lines.push('- `.planning/ROADMAP.md` — updated roadmap including the new milestone');
328
+ lines.push('- `.planning/PM_SIGNOFF.md` — planning sign-off with acceptance criteria');
329
+ lines.push('');
330
+ lines.push('Only after these artifacts exist and reference the new charter may you request `phase_transition_request: "implementation"`.');
331
+ lines.push('Do NOT repeat the idle-expansion proposal. Write the actual planning documents.');
332
+ lines.push('');
333
+ }
334
+
305
335
  const workflowResponsibilities = getWorkflowPromptResponsibilities(config, phase, roleId, root);
306
336
  if (workflowResponsibilities.length > 0) {
307
337
  const isReviewOnlyOwner = role.write_authority === 'review_only';
@@ -499,6 +529,12 @@ function renderPrompt(role, roleId, turn, state, config, root) {
499
529
  lines.push(`- **You are in the \`${currentPhase}\` phase.** When your work is complete${gateClause}, set \`phase_transition_request: "${nextPhase}"\` to advance to the next phase.`);
500
530
  } else if (phaseIdx === phaseNames.length - 1) {
501
531
  lines.push(`- **You are in the \`${currentPhase}\` phase (final phase).** When ready to ship, set \`run_completion_request: true\` and \`phase_transition_request: null\`.`);
532
+ if (runCompletionAutoApprovalApplies(config)) {
533
+ lines.push('- Run completion is governed by `approval_policy.run_completion.action: "auto_approve"` for this run.');
534
+ lines.push('- Do NOT set `status: "needs_human"` solely to request final run approval. If the required artifacts are complete and there are no genuine blockers, set `status: "completed"` with `run_completion_request: true`; the orchestrator will evaluate and auto-approve completion.');
535
+ } else {
536
+ lines.push('- If final approval is human-gated, set `run_completion_request: true` when ready; the orchestrator will route the approval gate.');
537
+ }
502
538
  }
503
539
  }
504
540
  // Phase-specific guidance for proposed roles
@@ -512,6 +548,12 @@ function renderPrompt(role, roleId, turn, state, config, root) {
512
548
  lines.push(`- **You are in the \`${currentPhase}\` phase (not final phase).** When your work is complete${gateClause}, set \`phase_transition_request: "${nextPhase}"\`.`);
513
549
  } else if (phaseIdx >= 0 && phaseIdx === phaseNames.length - 1) {
514
550
  lines.push(`- **You are in the \`${currentPhase}\` phase (final phase).** When ready to ship, set \`run_completion_request: true\` and \`phase_transition_request: null\`.`);
551
+ if (runCompletionAutoApprovalApplies(config)) {
552
+ lines.push('- Run completion is governed by `approval_policy.run_completion.action: "auto_approve"` for this run.');
553
+ lines.push('- Do NOT set `status: "needs_human"` solely to request final run approval. If the required artifacts are complete and there are no genuine blockers, set `status: "completed"` with `run_completion_request: true`; the orchestrator will evaluate and auto-approve completion.');
554
+ } else {
555
+ lines.push('- If final approval is human-gated, set `run_completion_request: true` when ready; the orchestrator will route the approval gate.');
556
+ }
515
557
  if (runtimeType === 'api_proxy' || runtimeType === 'remote_agent') {
516
558
  lines.push('- **Completion turns must be no-op:** set `proposed_changes` to `[]` or omit it, set `files_changed` to `[]`, and set `artifact.type` to `"review"`. Do NOT propose file changes on a completion turn.');
517
559
  }
@@ -5222,6 +5222,42 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5222
5222
  let gateResult = null;
5223
5223
  let completionResult = null;
5224
5224
  let timeoutResult = null;
5225
+
5226
+ // BUG-70: idle-expansion new_intake_intent is a proposal, not a chartered plan.
5227
+ // Suppress phase_transition_request — the new intake must be materialized into
5228
+ // planning artifacts by a subsequent turn before implementation can proceed.
5229
+ const isIdleExpansionNewIntake = idleExpansionResultSummary?.kind === 'new_intake_intent';
5230
+ if (isIdleExpansionNewIntake && turnResult.phase_transition_request) {
5231
+ const rawIdleResult = turnResult.idle_expansion_result;
5232
+ updatedState.charter_materialization_pending = {
5233
+ charter: rawIdleResult?.new_intake_intent?.charter
5234
+ || idleExpansionResultSummary.new_intent_title
5235
+ || null,
5236
+ acceptance_contract: rawIdleResult?.new_intake_intent?.acceptance_contract
5237
+ || [],
5238
+ suppressed_transition: turnResult.phase_transition_request,
5239
+ source_turn_id: turnResult.turn_id,
5240
+ recorded_at: now,
5241
+ };
5242
+ emitRunEvent(root, 'charter_materialization_required', {
5243
+ run_id: updatedState.run_id,
5244
+ phase: state.phase,
5245
+ status: updatedState.status,
5246
+ turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
5247
+ payload: {
5248
+ suppressed_transition: turnResult.phase_transition_request,
5249
+ reason: 'New intake from idle expansion must be materialized into planning artifacts before implementation dispatch.',
5250
+ new_intake_charter: updatedState.charter_materialization_pending.charter,
5251
+ },
5252
+ });
5253
+ }
5254
+
5255
+ // BUG-70: clear charter_materialization_pending when a non-idle-expansion turn
5256
+ // in the planning phase is accepted (the PM has had a chance to materialize).
5257
+ if (updatedState.charter_materialization_pending && !isIdleExpansionNewIntake && state.phase === 'planning') {
5258
+ updatedState.charter_materialization_pending = null;
5259
+ }
5260
+
5225
5261
  const hasRemainingTurns = Object.keys(remainingTurns).length > 0;
5226
5262
  if (turnResult.status !== 'needs_human') {
5227
5263
  if (hasRemainingTurns) {
@@ -5231,7 +5267,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5231
5267
  requested_at: now,
5232
5268
  };
5233
5269
  }
5234
- if (turnResult.phase_transition_request && !updatedState.queued_phase_transition) {
5270
+ if (turnResult.phase_transition_request && !updatedState.queued_phase_transition && !isIdleExpansionNewIntake) {
5235
5271
  updatedState.queued_phase_transition = {
5236
5272
  from: state.phase,
5237
5273
  to: turnResult.phase_transition_request,
@@ -5342,8 +5378,14 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5342
5378
  }
5343
5379
 
5344
5380
  if (updatedState.status !== 'blocked' && updatedState.status !== 'paused' && updatedState.status !== 'completed') {
5345
- const phaseSource = turnResult.phase_transition_request
5346
- ? turnResult
5381
+ // BUG-70: suppress phase transition for idle-expansion new_intake_intent turns.
5382
+ // Strip phase_transition_request from the effective turn so evaluatePhaseExit
5383
+ // sees no transition request and returns { action: 'no_request' }.
5384
+ const effectiveTurnResult = isIdleExpansionNewIntake
5385
+ ? { ...turnResult, phase_transition_request: null }
5386
+ : turnResult;
5387
+ const phaseSource = effectiveTurnResult.phase_transition_request
5388
+ ? effectiveTurnResult
5347
5389
  : findHistoryTurnRequest(nextHistoryEntries, state.queued_phase_transition?.requested_by_turn, 'phase_transition');
5348
5390
 
5349
5391
  // Always evaluate phase exit when the run drains — even without a request,
@@ -5351,7 +5393,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5351
5393
  gateResult = evaluatePhaseExit({
5352
5394
  state: postAcceptanceState,
5353
5395
  config,
5354
- acceptedTurn: phaseSource || turnResult,
5396
+ acceptedTurn: phaseSource || effectiveTurnResult,
5355
5397
  root,
5356
5398
  });
5357
5399
 
@@ -48,6 +48,7 @@ export const VALID_RUN_EVENTS = [
48
48
  'ghost_retry_exhausted',
49
49
  'state_reconciled_operator_commits',
50
50
  'operator_commit_reconcile_refused',
51
+ 'charter_materialization_required',
51
52
  ];
52
53
 
53
54
  /**