agentxchain 2.155.32 → 2.155.34
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
|
@@ -63,6 +63,19 @@ function getRoadmapReplenishmentTriageHints(root) {
|
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
function formatVisionSectionScope(sections, { limit = 5 } = {}) {
|
|
67
|
+
const names = Array.isArray(sections)
|
|
68
|
+
? sections.map((section) => String(section || '').trim()).filter(Boolean)
|
|
69
|
+
: [];
|
|
70
|
+
if (names.length === 0) {
|
|
71
|
+
return 'remaining VISION.md scope';
|
|
72
|
+
}
|
|
73
|
+
if (names.length <= limit) {
|
|
74
|
+
return names.join(', ');
|
|
75
|
+
}
|
|
76
|
+
return `${names.slice(0, limit).join(', ')} (+${names.length - limit} more)`;
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
// ---------------------------------------------------------------------------
|
|
67
80
|
// Session state
|
|
68
81
|
// ---------------------------------------------------------------------------
|
|
@@ -734,7 +747,8 @@ export function seedFromVision(root, visionPath, options = {}) {
|
|
|
734
747
|
// generic vision candidates can bypass PM roadmap replenishment.
|
|
735
748
|
const exhaustion = detectRoadmapExhaustedVisionOpen(root, visionPath);
|
|
736
749
|
if (exhaustion.open) {
|
|
737
|
-
const sectionNames = exhaustion.unplanned_sections
|
|
750
|
+
const sectionNames = formatVisionSectionScope(exhaustion.unplanned_sections);
|
|
751
|
+
const fullSectionNames = exhaustion.unplanned_sections.join(', ');
|
|
738
752
|
const replenishmentEvent = recordEvent(root, {
|
|
739
753
|
source: 'vision_scan',
|
|
740
754
|
category: 'roadmap_exhausted_vision_open',
|
|
@@ -745,7 +759,7 @@ export function seedFromVision(root, visionPath, options = {}) {
|
|
|
745
759
|
derived: true,
|
|
746
760
|
},
|
|
747
761
|
evidence: [
|
|
748
|
-
{ type: 'text', value: `All ${exhaustion.total_milestones} roadmap milestones checked. VISION sections not yet planned: ${
|
|
762
|
+
{ type: 'text', value: `All ${exhaustion.total_milestones} roadmap milestones checked. VISION sections not yet planned: ${fullSectionNames}` },
|
|
749
763
|
],
|
|
750
764
|
});
|
|
751
765
|
|
|
@@ -767,10 +781,10 @@ export function seedFromVision(root, visionPath, options = {}) {
|
|
|
767
781
|
template: 'generic',
|
|
768
782
|
...(replenishmentHints.preferred_role ? { preferred_role: replenishmentHints.preferred_role } : {}),
|
|
769
783
|
...(replenishmentHints.phase_scope ? { phase_scope: replenishmentHints.phase_scope } : {}),
|
|
770
|
-
charter: `[roadmap-replenishment] Derive next bounded roadmap increment from VISION.md.
|
|
784
|
+
charter: `[roadmap-replenishment] Derive next bounded roadmap increment from VISION.md. Candidate pool: ${sectionNames}. Current roadmap checked through ${exhaustion.latest_milestone}. Read .planning/VISION.md and .planning/ROADMAP.md to select one next testable milestone. Produce concrete unchecked M${exhaustion.total_milestones + 1} items. Do not re-verify previous completed milestones.`,
|
|
771
785
|
acceptance_contract: [
|
|
772
786
|
`New unchecked milestone items added to .planning/ROADMAP.md`,
|
|
773
|
-
`Milestone
|
|
787
|
+
`Milestone cites at least one concrete VISION.md source section from the unplanned backlog`,
|
|
774
788
|
`Milestone is bounded, testable, and does not duplicate existing checked milestones`,
|
|
775
789
|
],
|
|
776
790
|
});
|
|
@@ -795,7 +809,7 @@ export function seedFromVision(root, visionPath, options = {}) {
|
|
|
795
809
|
idle: false,
|
|
796
810
|
intentId: replenishmentIntentId,
|
|
797
811
|
section: 'Roadmap replenishment',
|
|
798
|
-
goal: `Derive next increment from: ${sectionNames}`,
|
|
812
|
+
goal: `Derive next increment from unplanned VISION scope: ${sectionNames}`,
|
|
799
813
|
source: 'roadmap_replenishment',
|
|
800
814
|
};
|
|
801
815
|
}
|
|
@@ -6995,6 +6995,12 @@ function evaluateIntentCoverage(turnResult, intakeContext, { state = null, confi
|
|
|
6995
6995
|
continue;
|
|
6996
6996
|
}
|
|
6997
6997
|
|
|
6998
|
+
const roadmapReplenishmentCoverage = evaluateRoadmapReplenishmentConditionalCoverage(item, turnResult, intakeContext);
|
|
6999
|
+
if (roadmapReplenishmentCoverage === true) {
|
|
7000
|
+
addressed.push(item);
|
|
7001
|
+
continue;
|
|
7002
|
+
}
|
|
7003
|
+
|
|
6998
7004
|
// Check 1: Structural — intent_response field with explicit status
|
|
6999
7005
|
const structuralEntry = responseMap.get(normalizedItem);
|
|
7000
7006
|
if (structuralEntry && ['addressed', 'deferred', 'rejected'].includes(structuralEntry.status)) {
|
|
@@ -7140,6 +7146,59 @@ function evaluateRoadmapDerivedConditionalCoverage(item, turnResult, intakeConte
|
|
|
7140
7146
|
return null;
|
|
7141
7147
|
}
|
|
7142
7148
|
|
|
7149
|
+
// ── Roadmap-replenishment conditional coverage (BUG-85) ───────────────────
|
|
7150
|
+
//
|
|
7151
|
+
// BUG-77's replenishment path originally generated one acceptance item
|
|
7152
|
+
// containing every unplanned VISION heading. A valid PM turn only binds one
|
|
7153
|
+
// next milestone, so requiring semantic coverage of the whole backlog is
|
|
7154
|
+
// overbroad. Accept both the new scoped item and the legacy broad item when
|
|
7155
|
+
// the result cites VISION.md and at least one concrete listed section.
|
|
7156
|
+
|
|
7157
|
+
function evaluateRoadmapReplenishmentConditionalCoverage(item, turnResult, intakeContext) {
|
|
7158
|
+
const charter = intakeContext?.charter || '';
|
|
7159
|
+
if (!charter.startsWith('[roadmap-replenishment]')) {
|
|
7160
|
+
return null;
|
|
7161
|
+
}
|
|
7162
|
+
|
|
7163
|
+
const normalizedItem = typeof item === 'string' ? item.toLowerCase().trim() : '';
|
|
7164
|
+
const isScopedTraceabilityItem =
|
|
7165
|
+
normalizedItem.startsWith('milestone cites at least one concrete vision.md source section')
|
|
7166
|
+
|| normalizedItem.startsWith('milestone scope derived from vision.md sections:');
|
|
7167
|
+
if (!isScopedTraceabilityItem) {
|
|
7168
|
+
return null;
|
|
7169
|
+
}
|
|
7170
|
+
|
|
7171
|
+
const corpus = [
|
|
7172
|
+
turnResult.summary || '',
|
|
7173
|
+
...(turnResult.decisions || []).map(d => `${d.statement || ''} ${d.rationale || ''}`),
|
|
7174
|
+
...(turnResult.objections || []).map(o => o.statement || ''),
|
|
7175
|
+
...(turnResult.files_changed || []),
|
|
7176
|
+
...(turnResult.artifacts_created || []),
|
|
7177
|
+
...(Array.isArray(turnResult.intent_response)
|
|
7178
|
+
? turnResult.intent_response.map(r => `${r.item || ''} ${r.detail || ''}`)
|
|
7179
|
+
: []),
|
|
7180
|
+
].join('\n').toLowerCase();
|
|
7181
|
+
|
|
7182
|
+
if (!corpus.includes('vision.md')) {
|
|
7183
|
+
return false;
|
|
7184
|
+
}
|
|
7185
|
+
|
|
7186
|
+
if (normalizedItem.startsWith('milestone cites at least one concrete vision.md source section')) {
|
|
7187
|
+
return true;
|
|
7188
|
+
}
|
|
7189
|
+
|
|
7190
|
+
const sectionList = String(item).split(':').slice(1).join(':');
|
|
7191
|
+
const sectionNames = sectionList
|
|
7192
|
+
.split(',')
|
|
7193
|
+
.map((section) => section.trim().toLowerCase())
|
|
7194
|
+
.filter((section) => section.length >= 4);
|
|
7195
|
+
if (sectionNames.length === 0) {
|
|
7196
|
+
return true;
|
|
7197
|
+
}
|
|
7198
|
+
|
|
7199
|
+
return sectionNames.some((section) => corpus.includes(section));
|
|
7200
|
+
}
|
|
7201
|
+
|
|
7143
7202
|
export {
|
|
7144
7203
|
STATE_PATH,
|
|
7145
7204
|
HISTORY_PATH,
|
|
@@ -1274,6 +1274,34 @@ export function normalizeTurnResult(tr, config, context = {}) {
|
|
|
1274
1274
|
);
|
|
1275
1275
|
normalized.proposed_next_role = fallback;
|
|
1276
1276
|
}
|
|
1277
|
+
} else if (
|
|
1278
|
+
// BUG-82: When BUG-81's gate auto-strip keeps the session in an earlier
|
|
1279
|
+
// phase (e.g. planning), authoritative roles dispatched in that phase may
|
|
1280
|
+
// propose a next role valid for the phase they *expected* (e.g. "qa" for
|
|
1281
|
+
// implementation) but illegal for the actual phase. Auto-normalize to a
|
|
1282
|
+
// routing-legal role instead of hard-erroring.
|
|
1283
|
+
isKnownPhase &&
|
|
1284
|
+
!isReviewOnly &&
|
|
1285
|
+
normalized.status === 'completed' &&
|
|
1286
|
+
typeof normalized.proposed_next_role === 'string' &&
|
|
1287
|
+
normalized.proposed_next_role !== 'human' &&
|
|
1288
|
+
!/^<[^>]+>$/.test(normalized.proposed_next_role) && // skip template placeholders — let schema validation catch them
|
|
1289
|
+
allowedNextRoles.length > 0 &&
|
|
1290
|
+
!allowedNextRoles.includes(normalized.proposed_next_role)
|
|
1291
|
+
) {
|
|
1292
|
+
const fallback = pickAllowedRoleFallback();
|
|
1293
|
+
if (fallback) {
|
|
1294
|
+
corrections.push(
|
|
1295
|
+
`proposed_next_role: corrected "${normalized.proposed_next_role}" to allowed role "${fallback}" (routing-illegal for phase "${currentPhase}")`
|
|
1296
|
+
);
|
|
1297
|
+
normalizationEvents.push({
|
|
1298
|
+
field: 'proposed_next_role',
|
|
1299
|
+
original_value: normalized.proposed_next_role,
|
|
1300
|
+
normalized_value: fallback,
|
|
1301
|
+
rationale: `routing_illegal_for_phase_${currentPhase}`,
|
|
1302
|
+
});
|
|
1303
|
+
normalized.proposed_next_role = fallback;
|
|
1304
|
+
}
|
|
1277
1305
|
}
|
|
1278
1306
|
|
|
1279
1307
|
return { normalized, corrections, normalizationEvents };
|