agentxchain 2.155.57 → 2.155.59

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.57",
3
+ "version": "2.155.59",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -506,6 +506,7 @@ function renderPrompt(role, roleId, turn, state, config, root) {
506
506
  lines.push('- `verification.status`: **REQUIRED**. One of `pass`, `fail`, `skipped`. Always include this field in the `verification` object.');
507
507
  lines.push('- `verification.status: "pass"` is valid only when every `verification.machine_evidence[].exit_code` is `0`, unless a negative-case command explicitly sets `expected_exit_code` to the same non-zero value.');
508
508
  lines.push('- Expected-failure checks should be wrapped in a verifier that exits `0` when the failure occurs as expected. If you must record a raw non-zero negative-case command on a passing turn, set `verification.machine_evidence[].expected_exit_code` to the expected non-zero code and explain it in `evidence_summary`.');
509
+ lines.push('- `verification.machine_evidence[]` is only for executable command records with both `command` and `exit_code`. Put typed marker checks, file-grep observations, bucket summaries, and acceptance-contract summaries in `verification.evidence_summary`, not in `machine_evidence`.');
509
510
  lines.push('- If verification commands produce side-effect files (e.g., `.tusq/plan.json`, `coverage/`, `.cache/`), declare each in `verification.produced_files` with `disposition: "ignore"` (temporary output to clean up) or `disposition: "artifact"` (output to checkpoint as a turn deliverable). Undeclared dirty files with declared verification will be auto-cleaned but declaring them is preferred.');
510
511
  lines.push('- `artifact.type`: **REQUIRED**. One of `workspace`, `patch`, `commit`, `review`.');
511
512
  lines.push('- If you make zero repo file edits, set `artifact.type` to `"review"` and `files_changed` to `[]`.');
@@ -7039,14 +7039,7 @@ function evaluateIntentCoverage(turnResult, intakeContext, { state = null, confi
7039
7039
  }
7040
7040
 
7041
7041
  // Build a searchable corpus from the turn result for semantic fallback
7042
- const corpus = [
7043
- turnResult.summary || '',
7044
- ...(turnResult.decisions || []).map(d => `${d.statement || ''} ${d.rationale || ''}`),
7045
- ...(turnResult.objections || []).map(o => o.statement || ''),
7046
- ...(turnResult.files_changed || []),
7047
- ...(turnResult.artifacts_created || []),
7048
- ...(Array.isArray(turnResult.intent_response) ? turnResult.intent_response.map(r => `${r.item || ''} ${r.detail || ''}`) : []),
7049
- ].join('\n').toLowerCase();
7042
+ const corpus = buildIntentCoverageCorpus(turnResult);
7050
7043
 
7051
7044
  for (const item of acceptanceItems) {
7052
7045
  const normalizedItem = item.toLowerCase().trim();
@@ -7085,7 +7078,7 @@ function evaluateIntentCoverage(turnResult, intakeContext, { state = null, confi
7085
7078
  }
7086
7079
 
7087
7080
  // Check 2: Semantic fallback — significant keyword overlap
7088
- const words = normalizedItem.split(/\s+/).filter(w => w.length > 3);
7081
+ const words = tokenizeIntentCoverageText(normalizedItem).filter(w => w.length > 3);
7089
7082
  if (words.length === 0) {
7090
7083
  addressed.push(item);
7091
7084
  continue;
@@ -7204,16 +7197,7 @@ function evaluateRoadmapDerivedConditionalCoverage(item, turnResult, intakeConte
7204
7197
  const milestoneId = sectionMatch[1].toLowerCase();
7205
7198
 
7206
7199
  // Build a searchable corpus from the turn result
7207
- const corpus = [
7208
- turnResult.summary || '',
7209
- ...(turnResult.decisions || []).map(d => `${d.statement || ''} ${d.rationale || ''}`),
7210
- ...(turnResult.objections || []).map(o => o.statement || ''),
7211
- ...(turnResult.files_changed || []),
7212
- ...(turnResult.artifacts_created || []),
7213
- ...(Array.isArray(turnResult.intent_response)
7214
- ? turnResult.intent_response.map(r => `${r.item || ''} ${r.detail || ''}`)
7215
- : []),
7216
- ].join('\n').toLowerCase();
7200
+ const corpus = buildIntentCoverageCorpus(turnResult);
7217
7201
 
7218
7202
  // If the turn mentions the milestone section ID, the item is addressed
7219
7203
  return corpus.includes(milestoneId);
@@ -7244,16 +7228,7 @@ function evaluateRoadmapReplenishmentConditionalCoverage(item, turnResult, intak
7244
7228
  return null;
7245
7229
  }
7246
7230
 
7247
- const corpus = [
7248
- turnResult.summary || '',
7249
- ...(turnResult.decisions || []).map(d => `${d.statement || ''} ${d.rationale || ''}`),
7250
- ...(turnResult.objections || []).map(o => o.statement || ''),
7251
- ...(turnResult.files_changed || []),
7252
- ...(turnResult.artifacts_created || []),
7253
- ...(Array.isArray(turnResult.intent_response)
7254
- ? turnResult.intent_response.map(r => `${r.item || ''} ${r.detail || ''}`)
7255
- : []),
7256
- ].join('\n').toLowerCase();
7231
+ const corpus = buildIntentCoverageCorpus(turnResult);
7257
7232
 
7258
7233
  if (!corpus.includes('vision.md')) {
7259
7234
  return false;
@@ -7275,6 +7250,40 @@ function evaluateRoadmapReplenishmentConditionalCoverage(item, turnResult, intak
7275
7250
  return sectionNames.some((section) => corpus.includes(section));
7276
7251
  }
7277
7252
 
7253
+ function buildIntentCoverageCorpus(turnResult) {
7254
+ const verification = turnResult?.verification && typeof turnResult.verification === 'object'
7255
+ ? turnResult.verification
7256
+ : {};
7257
+ const machineEvidenceText = Array.isArray(verification.machine_evidence)
7258
+ ? verification.machine_evidence.map((entry) => {
7259
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return '';
7260
+ return Object.values(entry).map((value) => {
7261
+ if (typeof value === 'string') return value;
7262
+ if (Array.isArray(value)) return value.join(' ');
7263
+ if (value && typeof value === 'object') return JSON.stringify(value);
7264
+ return '';
7265
+ }).join(' ');
7266
+ })
7267
+ : [];
7268
+
7269
+ return [
7270
+ turnResult.summary || '',
7271
+ ...(turnResult.decisions || []).map(d => `${d.statement || ''} ${d.rationale || ''}`),
7272
+ ...(turnResult.objections || []).map(o => o.statement || ''),
7273
+ ...(turnResult.files_changed || []),
7274
+ ...(turnResult.artifacts_created || []),
7275
+ verification.evidence_summary || '',
7276
+ ...machineEvidenceText,
7277
+ ...(Array.isArray(turnResult.intent_response) ? turnResult.intent_response.map(r => `${r.item || ''} ${r.detail || ''}`) : []),
7278
+ ].join('\n').toLowerCase();
7279
+ }
7280
+
7281
+ function tokenizeIntentCoverageText(value) {
7282
+ return String(value || '')
7283
+ .toLowerCase()
7284
+ .match(/[a-z0-9]+/g) || [];
7285
+ }
7286
+
7278
7287
  export {
7279
7288
  STATE_PATH,
7280
7289
  HISTORY_PATH,
@@ -1364,6 +1364,63 @@ export function normalizeTurnResult(tr, config, context = {}) {
1364
1364
  normalized.verification = { ...normalized.verification, status: inferredStatus };
1365
1365
  }
1366
1366
 
1367
+ // ── BUG-104: typed marker evidence belongs in evidence_summary ───────
1368
+ // machine_evidence is reserved for executable command records. Some model
1369
+ // turns emit useful structured observations in that array (for example
1370
+ // file_marker_grep objects) without command/exit_code fields. Preserve that
1371
+ // information as summary evidence instead of fabricating shell commands.
1372
+ if (
1373
+ normalized.verification
1374
+ && typeof normalized.verification === 'object'
1375
+ && !Array.isArray(normalized.verification)
1376
+ && Array.isArray(normalized.verification.machine_evidence)
1377
+ ) {
1378
+ const keptMachineEvidence = [];
1379
+ const structuredEvidenceSummaries = [];
1380
+ normalized.verification.machine_evidence.forEach((entry, index) => {
1381
+ if (
1382
+ entry
1383
+ && typeof entry === 'object'
1384
+ && !Array.isArray(entry)
1385
+ && typeof entry.command === 'string'
1386
+ && entry.command.trim()
1387
+ && Number.isInteger(entry.exit_code)
1388
+ ) {
1389
+ keptMachineEvidence.push(entry);
1390
+ return;
1391
+ }
1392
+
1393
+ const summary = summarizeStructuredMachineEvidence(entry);
1394
+ if (!summary) {
1395
+ keptMachineEvidence.push(entry);
1396
+ return;
1397
+ }
1398
+
1399
+ structuredEvidenceSummaries.push(summary);
1400
+ corrections.push(`verification.machine_evidence[${index}]: moved structured ${summary.type} evidence into evidence_summary`);
1401
+ normalizationEvents.push({
1402
+ field: `verification.machine_evidence[${index}]`,
1403
+ original_value: entry,
1404
+ normalized_value: summary.text,
1405
+ rationale: 'structured_machine_evidence_moved_to_evidence_summary',
1406
+ });
1407
+ });
1408
+
1409
+ if (structuredEvidenceSummaries.length > 0) {
1410
+ const existingSummary = typeof normalized.verification.evidence_summary === 'string'
1411
+ ? normalized.verification.evidence_summary.trim()
1412
+ : '';
1413
+ const appendedSummary = structuredEvidenceSummaries.map((summary) => summary.text).join('; ');
1414
+ normalized.verification = {
1415
+ ...normalized.verification,
1416
+ machine_evidence: keptMachineEvidence,
1417
+ evidence_summary: existingSummary
1418
+ ? `${existingSummary}\nStructured evidence: ${appendedSummary}`
1419
+ : `Structured evidence: ${appendedSummary}`,
1420
+ };
1421
+ }
1422
+ }
1423
+
1367
1424
  // ── BUG-90: normalize missing artifact.type ─────────────────────────
1368
1425
  if (
1369
1426
  normalized.artifact
@@ -1742,6 +1799,42 @@ export function normalizeTurnResult(tr, config, context = {}) {
1742
1799
  return { normalized, corrections, normalizationEvents };
1743
1800
  }
1744
1801
 
1802
+ function summarizeStructuredMachineEvidence(entry) {
1803
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
1804
+ return null;
1805
+ }
1806
+ if (typeof entry.type !== 'string' || entry.type.trim().length === 0) {
1807
+ return null;
1808
+ }
1809
+
1810
+ const type = entry.type.trim();
1811
+ const parts = [`type=${type}`];
1812
+ for (const key of ['path', 'marker', 'section', 'result']) {
1813
+ if (typeof entry[key] === 'string' && entry[key].trim()) {
1814
+ parts.push(`${key}=${truncateEvidenceValue(entry[key].trim())}`);
1815
+ }
1816
+ }
1817
+ if (Array.isArray(entry.contract) && entry.contract.length > 0) {
1818
+ parts.push(`contract=${truncateEvidenceValue(entry.contract.join(' | '))}`);
1819
+ }
1820
+ if (
1821
+ entry.buckets_observed
1822
+ && typeof entry.buckets_observed === 'object'
1823
+ && !Array.isArray(entry.buckets_observed)
1824
+ ) {
1825
+ parts.push(`buckets_observed=${truncateEvidenceValue(JSON.stringify(entry.buckets_observed))}`);
1826
+ }
1827
+
1828
+ return {
1829
+ type,
1830
+ text: `[${parts.join(', ')}]`,
1831
+ };
1832
+ }
1833
+
1834
+ function truncateEvidenceValue(value, max = 220) {
1835
+ return value.length > max ? `${value.slice(0, max - 3)}...` : value;
1836
+ }
1837
+
1745
1838
  function normalizeIdleExpansionMutualExclusionSentinel(result) {
1746
1839
  if (!result || typeof result !== 'object' || Array.isArray(result)) {
1747
1840
  return { changed: false, value: result, correction: '' };