@autobe/agent
Version:
AI backend server code generator
105 lines • 3.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildFileDecisionConflictMap = exports.detectDecisionConflicts = void 0;
// ─── Main Detection ───
/**
* Detect decision-level conflicts across all files.
*
* Groups all extracted decisions by `topic + decision` key, then finds cases
* where different files assign different values to the same key.
*
* This catches prose-level contradictions like:
*
* - File A: "password change requires current password" (yes)
* - File B: "password change does not require current password" (no)
*/
const detectDecisionConflicts = (props) => {
// Group by topic+decision
const groups = new Map();
for (const { filename, decisions } of props.fileDecisions) {
for (const d of decisions) {
const key = `${normalizeKey(d.topic)}::${normalizeKey(d.decision)}`;
if (!groups.has(key))
groups.set(key, []);
groups.get(key).push({
value: normalizeValue(d.value),
filename,
evidence: d.evidence,
});
}
}
// Find conflicts: same key, different values
const conflicts = [];
for (const [key, entries] of groups) {
// Group entries by value
const byValue = new Map();
for (const entry of entries) {
if (!byValue.has(entry.value))
byValue.set(entry.value, []);
byValue.get(entry.value).push({
filename: entry.filename,
evidence: entry.evidence,
});
}
// If more than one distinct value exists → conflict
if (byValue.size <= 1)
continue;
const [topic, decision] = key.split("::");
conflicts.push({
topic: topic,
decision: decision,
values: [...byValue.entries()].map(([value, sources]) => {
var _a, _b;
return ({
value,
files: sources.map((s) => s.filename),
evidence: (_b = (_a = sources[0]) === null || _a === void 0 ? void 0 : _a.evidence) !== null && _b !== void 0 ? _b : "",
});
}),
});
}
return conflicts;
};
exports.detectDecisionConflicts = detectDecisionConflicts;
/**
* Build a map from filename → list of decision conflict feedback strings.
*
* Each file involved in a conflict gets feedback describing the contradiction.
*/
const buildFileDecisionConflictMap = (conflicts) => {
const map = new Map();
for (const conflict of conflicts) {
const valueSummary = conflict.values
.map((v) => `"${v.value}" in [${v.files.join(", ")}]`)
.join(" vs ");
const feedback = `Decision conflict: ${conflict.topic}.${conflict.decision} — ${valueSummary}. ` +
`Files must agree on this decision. Align with the canonical source.`;
// Add feedback to ALL files involved in this conflict
for (const valueGroup of conflict.values) {
for (const filename of valueGroup.files) {
if (!map.has(filename))
map.set(filename, []);
map.get(filename).push(feedback);
}
}
}
return map;
};
exports.buildFileDecisionConflictMap = buildFileDecisionConflictMap;
// ─── Helpers ───
/**
* Normalize a key string for grouping: lowercase, replace whitespace/special
* chars with underscore, trim.
*/
function normalizeKey(s) {
return s
.toLowerCase()
.trim()
.replace(/[\s\-\.]+/g, "_")
.replace(/[^a-z0-9_]/g, "");
}
/** Normalize a value for comparison: lowercase, trim, collapse whitespace. */
function normalizeValue(s) {
return s.toLowerCase().trim().replace(/\s+/g, " ");
}
//# sourceMappingURL=detectDecisionConflicts.js.map