agentxchain 2.29.0 → 2.30.0

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.29.0",
3
+ "version": "2.30.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@ const SECTION_DEFINITIONS = [
10
10
  { id: 'last_turn_verification', header: null, required: false },
11
11
  { id: 'blockers', header: 'Blockers', required: true },
12
12
  { id: 'escalation', header: 'Escalation', required: true },
13
+ { id: 'workflow_artifacts', header: 'Workflow Artifacts', required: false },
13
14
  { id: 'gate_required_files', header: 'Gate Required Files', required: false },
14
15
  { id: 'phase_gate_status', header: 'Phase Gate Status', required: false },
15
16
  ];
@@ -88,6 +89,7 @@ export function renderContextSections(sections) {
88
89
 
89
90
  appendTopLevelSection(lines, 'Blockers', [sectionMap.get('blockers')?.content]);
90
91
  appendTopLevelSection(lines, 'Escalation', [sectionMap.get('escalation')?.content]);
92
+ appendTopLevelSection(lines, 'Workflow Artifacts', [sectionMap.get('workflow_artifacts')?.content]);
91
93
  appendTopLevelSection(lines, 'Gate Required Files', [sectionMap.get('gate_required_files')?.content]);
92
94
  appendTopLevelSection(lines, 'Phase Gate Status', [sectionMap.get('phase_gate_status')?.content]);
93
95
 
@@ -248,6 +248,31 @@ function renderPrompt(role, roleId, turn, state, config, root) {
248
248
  lines.push('');
249
249
  }
250
250
 
251
+ const workflowResponsibilities = getWorkflowPromptResponsibilities(config, phase, roleId, root);
252
+ if (workflowResponsibilities.length > 0) {
253
+ const isReviewOnlyOwner = role.write_authority === 'review_only';
254
+ lines.push('## Workflow-Kit Responsibilities');
255
+ lines.push('');
256
+ if (isReviewOnlyOwner) {
257
+ lines.push(`You are accountable for reviewing and attesting to these workflow-kit artifacts in phase \`${phase}\`:`);
258
+ } else {
259
+ lines.push(`You are accountable for producing these workflow-kit artifacts in phase \`${phase}\`:`);
260
+ }
261
+ lines.push('');
262
+ for (const artifact of workflowResponsibilities) {
263
+ const requiredLabel = artifact.required ? 'required' : 'optional';
264
+ const semanticsLabel = artifact.semantics ? `\`${artifact.semantics}\`` : '—';
265
+ lines.push(`- \`${artifact.path}\` — ${requiredLabel}; semantics: ${semanticsLabel}; status: ${artifact.status}`);
266
+ }
267
+ lines.push('');
268
+ if (isReviewOnlyOwner) {
269
+ lines.push('You cannot write repo files directly. Your accountability means you must confirm these artifacts exist, meet quality standards, and satisfy their semantic requirements. If a required artifact you own is missing, escalate to the producing role — do not request phase transition.');
270
+ } else {
271
+ lines.push('Do not request phase transition or run completion while a required workflow-kit artifact you own is missing or incomplete.');
272
+ }
273
+ lines.push('');
274
+ }
275
+
251
276
  // Gate requirements
252
277
  if (gateConfig) {
253
278
  lines.push('## Phase Exit Gate');
@@ -421,6 +446,47 @@ function renderPrompt(role, roleId, turn, state, config, root) {
421
446
  };
422
447
  }
423
448
 
449
+ function getWorkflowPromptResponsibilities(config, phase, roleId, root) {
450
+ const artifacts = config?.workflow_kit?.phases?.[phase]?.artifacts;
451
+ if (!Array.isArray(artifacts) || artifacts.length === 0) {
452
+ return [];
453
+ }
454
+
455
+ const entryRole = config?.routing?.[phase]?.entry_role || null;
456
+ const responsibilities = [];
457
+
458
+ for (const artifact of artifacts) {
459
+ if (!artifact?.path) {
460
+ continue;
461
+ }
462
+
463
+ const owner = typeof artifact.owned_by === 'string' && artifact.owned_by
464
+ ? artifact.owned_by
465
+ : null;
466
+ const responsibleRole = owner || entryRole;
467
+ if (responsibleRole !== roleId) {
468
+ continue;
469
+ }
470
+
471
+ const absPath = join(root, artifact.path);
472
+ let exists = false;
473
+ try {
474
+ exists = existsSync(absPath);
475
+ } catch {
476
+ exists = false;
477
+ }
478
+
479
+ responsibilities.push({
480
+ path: artifact.path,
481
+ required: artifact.required !== false,
482
+ semantics: artifact.semantics || null,
483
+ status: exists ? 'exists' : 'MISSING',
484
+ });
485
+ }
486
+
487
+ return responsibilities;
488
+ }
489
+
424
490
  // ── Context Rendering ───────────────────────────────────────────────────────
425
491
 
426
492
  function renderContext(state, config, root, turn, role) {
@@ -621,8 +687,59 @@ function renderContext(state, config, root, turn, role) {
621
687
  lines.push('');
622
688
  }
623
689
 
624
- // Phase gate requirements
690
+ // Workflow-kit artifacts for the current phase
625
691
  const phase = state.phase;
692
+ const wkArtifacts = config?.workflow_kit?.phases?.[phase]?.artifacts;
693
+ if (Array.isArray(wkArtifacts) && wkArtifacts.length > 0) {
694
+ lines.push('## Workflow Artifacts');
695
+ lines.push('');
696
+ lines.push(`Current phase **${phase}** declares the following artifacts:`);
697
+ lines.push('');
698
+ lines.push('| Artifact | Required | Semantics | Owner | Status |');
699
+ lines.push('|----------|----------|-----------|-------|--------|');
700
+ const isReviewRole = role?.write_authority === 'review_only';
701
+ const reviewPreviews = [];
702
+ for (const art of wkArtifacts) {
703
+ if (!art?.path) continue;
704
+ const absPath = join(root, art.path);
705
+ let exists = false;
706
+ try { exists = existsSync(absPath); } catch { /* treat as missing */ }
707
+ const req = art.required !== false ? 'yes' : 'no';
708
+ const owner = art.owned_by || '—';
709
+ const status = exists ? 'exists' : 'MISSING';
710
+ const semCol = art.semantics ? `\`${art.semantics}\`` : '—';
711
+ lines.push(`| \`${art.path}\` | ${req} | ${semCol} | ${owner} | ${status} |`);
712
+ if (isReviewRole && exists) {
713
+ reviewPreviews.push(art);
714
+ }
715
+ }
716
+ lines.push('');
717
+ if (reviewPreviews.length > 0) {
718
+ for (const art of reviewPreviews) {
719
+ const absPath = join(root, art.path);
720
+ const preview = buildGateFilePreview(absPath);
721
+ if (preview) {
722
+ lines.push(`### \`${art.path}\``);
723
+ lines.push('');
724
+ const semantic = extractGateFileSemantic(art.path, preview.raw);
725
+ if (semantic) {
726
+ lines.push(`**Semantic: ${semantic}**`);
727
+ lines.push('');
728
+ }
729
+ lines.push('```');
730
+ lines.push(preview.content);
731
+ lines.push('```');
732
+ if (preview.truncated) {
733
+ lines.push('');
734
+ lines.push(`_Preview truncated after ${GATE_FILE_PREVIEW_MAX_LINES} lines._`);
735
+ }
736
+ lines.push('');
737
+ }
738
+ }
739
+ }
740
+ }
741
+
742
+ // Phase gate requirements
626
743
  const routing = config.routing?.[phase];
627
744
  const exitGate = routing?.exit_gate;
628
745
  const gateConfig = exitGate ? config.gates?.[exitGate] : null;
@@ -628,6 +628,25 @@ export function validateWorkflowKitConfig(wk, routing, roles) {
628
628
  errors.push(`${prefix} owned_by "${artifact.owned_by}" is not a valid role ID (must be lowercase alphanumeric with hyphens/underscores)`);
629
629
  } else if (roles && typeof roles === 'object' && !roles[artifact.owned_by]) {
630
630
  errors.push(`${prefix} owned_by "${artifact.owned_by}" does not reference a defined role`);
631
+ } else if (
632
+ artifact.required !== false &&
633
+ roles && typeof roles === 'object' &&
634
+ roles[artifact.owned_by]?.write_authority === 'review_only'
635
+ ) {
636
+ // Check if any authoritative/proposed role exists in this phase's routing
637
+ const phaseRouting = routing?.[phase];
638
+ const phaseRoles = new Set([
639
+ ...(phaseRouting?.allowed_next_roles || []),
640
+ ...(phaseRouting?.entry_role ? [phaseRouting.entry_role] : []),
641
+ ]);
642
+ const hasWriter = [...phaseRoles].some(rid =>
643
+ roles[rid]?.write_authority === 'authoritative' || roles[rid]?.write_authority === 'proposed',
644
+ );
645
+ if (!hasWriter) {
646
+ warnings.push(
647
+ `${prefix} owned_by "${artifact.owned_by}" is a review_only role in phase "${phase}" with no authoritative or proposed role — nobody can write this required artifact`,
648
+ );
649
+ }
631
650
  }
632
651
  }
633
652
  }