agentxchain 2.155.30 → 2.155.32
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 +1 -1
- package/src/lib/governed-state.js +112 -22
package/package.json
CHANGED
|
@@ -4701,29 +4701,51 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
4701
4701
|
|
|
4702
4702
|
const gateSemanticMode = config.gate_semantic_coverage_mode || 'strict';
|
|
4703
4703
|
if (uncoveredFiles.length > 0 && gateSemanticMode === 'strict') {
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4704
|
+
// BUG-81: In planning phase only, auto-strip the phase transition request
|
|
4705
|
+
// instead of permanently blocking. PM's partial planning work is preserved;
|
|
4706
|
+
// the phase stays in "planning" and the continuous loop re-dispatches PM to
|
|
4707
|
+
// complete gate artifacts. In implementation+ phases, gate failures still
|
|
4708
|
+
// reject — dev/qa claiming readiness without updating gate files is a real error.
|
|
4709
|
+
if (turnResult.phase_transition_request && state.phase === 'planning') {
|
|
4710
|
+
emitRunEvent(root, 'gate_transition_request_auto_stripped', {
|
|
4711
|
+
run_id: state.run_id,
|
|
4712
|
+
phase: state.phase,
|
|
4713
|
+
status: state.status,
|
|
4714
|
+
turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
|
|
4715
|
+
payload: {
|
|
4716
|
+
gate_id: exitGateId,
|
|
4717
|
+
uncovered_files: uncoveredFiles,
|
|
4718
|
+
declared_files: [...declaredFiles],
|
|
4719
|
+
rationale: 'Turn did not modify gate-required files; phase transition stripped to preserve partial planning work',
|
|
4720
|
+
},
|
|
4721
|
+
});
|
|
4722
|
+
delete turnResult.phase_transition_request;
|
|
4723
|
+
// Fall through — continue with acceptance without the phase transition
|
|
4724
|
+
} else {
|
|
4725
|
+
const coverageError = `Gate "${exitGateId}" is failing on ${uncoveredFiles.join(', ')}. Your turn did not modify ${uncoveredFiles.length === 1 ? 'that file' : 'those files'}. Either edit the file(s) to satisfy the gate, or remove the phase transition request.`;
|
|
4726
|
+
transitionToFailedAcceptance(root, state, currentTurn, coverageError, {
|
|
4727
|
+
error_code: 'gate_semantic_coverage',
|
|
4721
4728
|
stage: 'gate_semantic_coverage',
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4729
|
+
extra: {
|
|
4730
|
+
gate_id: exitGateId,
|
|
4731
|
+
uncovered_files: uncoveredFiles,
|
|
4732
|
+
declared_files: [...declaredFiles],
|
|
4733
|
+
gate_reasons: preGateResult.reasons,
|
|
4734
|
+
},
|
|
4735
|
+
});
|
|
4736
|
+
return {
|
|
4737
|
+
ok: false,
|
|
4738
|
+
error: coverageError,
|
|
4739
|
+
validation: {
|
|
4740
|
+
...validation,
|
|
4741
|
+
ok: false,
|
|
4742
|
+
stage: 'gate_semantic_coverage',
|
|
4743
|
+
error_class: 'gate_coverage_error',
|
|
4744
|
+
errors: uncoveredFiles.map(f => `Gate "${exitGateId}" is failing on "${f}". Your turn did not modify that file.`),
|
|
4745
|
+
warnings: [],
|
|
4746
|
+
},
|
|
4747
|
+
};
|
|
4748
|
+
}
|
|
4727
4749
|
}
|
|
4728
4750
|
}
|
|
4729
4751
|
}
|
|
@@ -6967,6 +6989,12 @@ function evaluateIntentCoverage(turnResult, intakeContext, { state = null, confi
|
|
|
6967
6989
|
continue;
|
|
6968
6990
|
}
|
|
6969
6991
|
|
|
6992
|
+
const roadmapCoverage = evaluateRoadmapDerivedConditionalCoverage(item, turnResult, intakeContext, state);
|
|
6993
|
+
if (roadmapCoverage === true) {
|
|
6994
|
+
addressed.push(item);
|
|
6995
|
+
continue;
|
|
6996
|
+
}
|
|
6997
|
+
|
|
6970
6998
|
// Check 1: Structural — intent_response field with explicit status
|
|
6971
6999
|
const structuralEntry = responseMap.get(normalizedItem);
|
|
6972
7000
|
if (structuralEntry && ['addressed', 'deferred', 'rejected'].includes(structuralEntry.status)) {
|
|
@@ -7050,6 +7078,68 @@ function evaluateIdleExpansionConditionalCoverage(item, turnResult, intakeContex
|
|
|
7050
7078
|
return null;
|
|
7051
7079
|
}
|
|
7052
7080
|
|
|
7081
|
+
// ── Roadmap-derived intent conditional coverage (BUG-80) ────────────────────
|
|
7082
|
+
//
|
|
7083
|
+
// Roadmap-derived intents (charter starts with "[roadmap]") include literal
|
|
7084
|
+
// implementation text as acceptance items. PM turns in planning phase produce
|
|
7085
|
+
// charter/scoping artifacts, not implementation code, so the generic 50%
|
|
7086
|
+
// keyword-overlap check fails. This function provides phase-aware evaluation:
|
|
7087
|
+
// - "Evidence source:" items are metadata provenance → always addressed
|
|
7088
|
+
// - "Unchecked roadmap item completed:" in planning phase → milestone-mention check
|
|
7089
|
+
// - In implementation+ phases → falls through to normal semantic matching
|
|
7090
|
+
|
|
7091
|
+
function evaluateRoadmapDerivedConditionalCoverage(item, turnResult, intakeContext, state) {
|
|
7092
|
+
const charter = intakeContext?.charter || '';
|
|
7093
|
+
if (!charter.startsWith('[roadmap]')) {
|
|
7094
|
+
return null;
|
|
7095
|
+
}
|
|
7096
|
+
|
|
7097
|
+
const normalizedItem = typeof item === 'string' ? item.toLowerCase().trim() : '';
|
|
7098
|
+
|
|
7099
|
+
// "Evidence source:" items are provenance metadata, not deliverables.
|
|
7100
|
+
// No turn of any role can "address" a file path reference.
|
|
7101
|
+
if (normalizedItem.startsWith('evidence source:')) {
|
|
7102
|
+
return true;
|
|
7103
|
+
}
|
|
7104
|
+
|
|
7105
|
+
// "Unchecked roadmap item completed:" contains literal implementation text.
|
|
7106
|
+
// In planning phase, PM scopes the milestone — check for milestone mention
|
|
7107
|
+
// rather than requiring implementation-keyword overlap.
|
|
7108
|
+
// In implementation/qa/launch phases, fall through to normal semantic matching
|
|
7109
|
+
// so dev turns are still evaluated against actual implementation keywords.
|
|
7110
|
+
if (normalizedItem.startsWith('unchecked roadmap item completed:')) {
|
|
7111
|
+
const currentPhase = state?.phase || '';
|
|
7112
|
+
if (currentPhase !== 'planning') {
|
|
7113
|
+
return null;
|
|
7114
|
+
}
|
|
7115
|
+
|
|
7116
|
+
// Extract milestone section identifier from charter: "[roadmap] M28: ..."
|
|
7117
|
+
const sectionMatch = charter.match(/\[roadmap\]\s*(M\d+)/i);
|
|
7118
|
+
if (!sectionMatch) {
|
|
7119
|
+
return null;
|
|
7120
|
+
}
|
|
7121
|
+
|
|
7122
|
+
const milestoneId = sectionMatch[1].toLowerCase();
|
|
7123
|
+
|
|
7124
|
+
// Build a searchable corpus from the turn result
|
|
7125
|
+
const corpus = [
|
|
7126
|
+
turnResult.summary || '',
|
|
7127
|
+
...(turnResult.decisions || []).map(d => `${d.statement || ''} ${d.rationale || ''}`),
|
|
7128
|
+
...(turnResult.objections || []).map(o => o.statement || ''),
|
|
7129
|
+
...(turnResult.files_changed || []),
|
|
7130
|
+
...(turnResult.artifacts_created || []),
|
|
7131
|
+
...(Array.isArray(turnResult.intent_response)
|
|
7132
|
+
? turnResult.intent_response.map(r => `${r.item || ''} ${r.detail || ''}`)
|
|
7133
|
+
: []),
|
|
7134
|
+
].join('\n').toLowerCase();
|
|
7135
|
+
|
|
7136
|
+
// If the turn mentions the milestone section ID, the item is addressed
|
|
7137
|
+
return corpus.includes(milestoneId);
|
|
7138
|
+
}
|
|
7139
|
+
|
|
7140
|
+
return null;
|
|
7141
|
+
}
|
|
7142
|
+
|
|
7053
7143
|
export {
|
|
7054
7144
|
STATE_PATH,
|
|
7055
7145
|
HISTORY_PATH,
|