reasoning: ground unclassified answers without category expansion

This commit is contained in:
2026-08-10 08:20:00 +01:00
parent 4ea664d0d8
commit 69efc5d1b9
2 changed files with 63 additions and 5 deletions
+18 -3
View File
@@ -2804,6 +2804,16 @@ function proposalResolutionSummary(proposal) {
return { resolved, proposalText };
}
function resolvedProposalMeaningText(proposal) {
return normaliseSemanticText(
(proposal.updatedNodes || [])
.filter((update) => update.newStatus === "resolved")
.map((update) => update.newValue)
.filter((value) => typeof value === "string" && value.trim().length > 0)
.join(" "),
);
}
function mentionsHardConstraint(text) {
return (
text.includes("hard constraint") ||
@@ -3040,13 +3050,18 @@ function validateAnswerMeaningAlignment(proposal) {
}
if (supportCategory === "other") {
const resolvedMeaningText = resolvedProposalMeaningText(proposal);
if (
resolved &&
containsConstraintBoundaryLanguage(proposalText) &&
!containsConstraintBoundaryLanguage(meaningText)
resolvedMeaningText &&
!rawAnswerSupportsUnclassifiedMeaning(
proposal.answerMeaning.userSupportedMeaning,
resolvedMeaningText,
)
) {
errors.push(
"Proposal cannot resolve beyond an unclassified answer by introducing an unsupported constraint or preference/trade-off distinction.",
"Proposal cannot resolve beyond an unclassified answer by introducing unsupported stronger meaning than answerMeaning.userSupportedMeaning establishes.",
);
}
}
+45 -2
View File
@@ -1568,7 +1568,7 @@ describe("applyValidatedProposal", () => {
);
});
it("57A regression: a legitimate decision-advancing answer classified as other may resolve its intended unknown", () => {
it("57F legitimate-answer regression: a grounded unclassified answer may resolve its intended unknown", () => {
const graph = makeCommercialUpdateFixture();
const parentId = graph.activeUnknownNodeId;
@@ -1640,6 +1640,49 @@ describe("applyValidatedProposal", () => {
expect(result.stage).toBeUndefined();
});
it("57F deterministic reproduction: pre-fix grounded unclassified answer would have been rejected by proposal-text constraint language", () => {
const graph = makeCommercialUpdateFixture();
const parentId = graph.activeUnknownNodeId;
const result = applyValidatedProposal({
situationGraph: graph,
answer:
"We're looking at this mainly for cost reduction — roughly £2M annual savings on office overhead.",
previousQuestion:
"What problem would this need to solve to justify continuing development?",
proposal: {
addedNodes: [],
updatedNodes: [
{
nodeId: parentId,
previousStatus: "unknown",
newStatus: "resolved",
previousValue: null,
newValue:
"Cost reduction, including approximately £2M annual office-overhead savings, is a stated reason for considering the relocation.",
reason:
"The answer establishes that cost reduction is the relevant preference/trade-off consideration for this decision.",
},
],
addedEdges: [],
removedEdgeIds: [],
resolvedUnknownNodeIds: [parentId],
affectedNodeIds: [],
selectedQuestion: null,
answerMeaning: {
userSupportedMeaning:
"Cost reduction, including approximately £2M annual office-overhead savings, is a stated reason for considering the relocation.",
possibleInference: null,
supportCategory: "other",
resolutionGuidance: null,
},
},
});
expect(result.success).toBe(true);
expect(result.updatedSituationGraph.resolvedNodeIds).toContain(parentId);
});
it("unsafe unclassified answer: other is not automatically trusted when the proposal adds stronger unsupported meaning", () => {
const graph = makeCommercialUpdateFixture();
const parentId = graph.activeUnknownNodeId;
@@ -1902,7 +1945,7 @@ describe("applyValidatedProposal", () => {
expect(result.success).toBe(false);
expect(result.stage).toBe("proposal_compatibility");
expect(result.errors.join(" ")).toContain(
"unsupported constraint or preference/trade-off distinction",
"unsupported stronger meaning than answerMeaning.userSupportedMeaning establishes",
);
});