refine answer meaning derivation for negation and qualification
This commit is contained in:
+56
-12
@@ -2745,6 +2745,46 @@ function proposalResolutionSummary(proposal) {
|
||||
return { resolved, proposalText };
|
||||
}
|
||||
|
||||
function mentionsHardConstraint(text) {
|
||||
return (
|
||||
text.includes("hard constraint") ||
|
||||
text.includes("non-negotiable") ||
|
||||
text.includes("dont want any increase in risk") ||
|
||||
text.includes("do not want any increase in risk")
|
||||
);
|
||||
}
|
||||
|
||||
function mentionsNegatedHardConstraint(text) {
|
||||
return (
|
||||
text.includes("rather than a hard constraint") ||
|
||||
text.includes("not a hard constraint") ||
|
||||
text.includes("not an absolute constraint") ||
|
||||
text.includes("preference rather than a hard constraint")
|
||||
);
|
||||
}
|
||||
|
||||
function hasDefaultPreferenceSignal(text) {
|
||||
return (
|
||||
text.includes("preference") ||
|
||||
text.includes("normally") ||
|
||||
text.includes("default preference") ||
|
||||
text.includes("would usually") ||
|
||||
text.includes("tend to")
|
||||
);
|
||||
}
|
||||
|
||||
function hasExceptionOrOverrideSignal(text) {
|
||||
return (
|
||||
text.includes(" but ") ||
|
||||
text.includes(" if ") ||
|
||||
text.includes("override") ||
|
||||
text.includes("overridden") ||
|
||||
text.includes("willing to accept") ||
|
||||
text.includes("willingness to accept") ||
|
||||
text.includes("accept some risk")
|
||||
);
|
||||
}
|
||||
|
||||
function deriveAnswerMeaningProfile(userSupportedMeaning) {
|
||||
const meaningText = normaliseSemanticText(userSupportedMeaning);
|
||||
|
||||
@@ -2761,25 +2801,29 @@ function deriveAnswerMeaningProfile(userSupportedMeaning) {
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
meaningText.includes("hard constraint") ||
|
||||
meaningText.includes("non-negotiable") ||
|
||||
meaningText.includes("dont want any increase in risk") ||
|
||||
meaningText.includes("do not want any increase in risk")
|
||||
) {
|
||||
return {
|
||||
category: "explicit_hard_constraint",
|
||||
resolutionGuidance: "must_resolve",
|
||||
};
|
||||
}
|
||||
const negatedHardConstraint = mentionsNegatedHardConstraint(meaningText);
|
||||
const affirmativeHardConstraint =
|
||||
mentionsHardConstraint(meaningText) && !negatedHardConstraint;
|
||||
const conditionalPreferenceStructure =
|
||||
(hasDefaultPreferenceSignal(meaningText) &&
|
||||
hasExceptionOrOverrideSignal(meaningText)) ||
|
||||
(negatedHardConstraint && hasExceptionOrOverrideSignal(meaningText)) ||
|
||||
hasConditionalQualification(meaningText);
|
||||
|
||||
if (hasConditionalQualification(meaningText)) {
|
||||
if (conditionalPreferenceStructure) {
|
||||
return {
|
||||
category: "conditional_tradeoff",
|
||||
resolutionGuidance: "may_resolve",
|
||||
};
|
||||
}
|
||||
|
||||
if (affirmativeHardConstraint) {
|
||||
return {
|
||||
category: "explicit_hard_constraint",
|
||||
resolutionGuidance: "must_resolve",
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
meaningText.includes("matters more") ||
|
||||
meaningText.includes("more important") ||
|
||||
|
||||
@@ -1212,6 +1212,100 @@ describe("applyValidatedProposal", () => {
|
||||
expect(result.errors.join(" ")).toContain("conditional qualification");
|
||||
});
|
||||
|
||||
it("B live-variant 1: negated hard-constraint mention stays conditional rather than explicit hard constraint", () => {
|
||||
const { graph, riskUnknownId } = makeRiskClarificationFixture();
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
answer:
|
||||
"I'd normally avoid more risk, but for the right opportunity I might accept some.",
|
||||
previousQuestion:
|
||||
"Is avoiding additional risk a hard constraint or a preference/trade-off?",
|
||||
proposal: {
|
||||
addedNodes: [],
|
||||
updatedNodes: [
|
||||
{
|
||||
nodeId: riskUnknownId,
|
||||
previousStatus: "unknown",
|
||||
newStatus: "resolved",
|
||||
previousValue: null,
|
||||
newValue:
|
||||
"Avoiding additional risk is a preference or trade-off rather than a hard constraint.",
|
||||
reason:
|
||||
"The answer shows a preference or trade-off rather than a hard constraint.",
|
||||
},
|
||||
],
|
||||
addedEdges: [],
|
||||
removedEdgeIds: [],
|
||||
resolvedUnknownNodeIds: [riskUnknownId],
|
||||
affectedNodeIds: [],
|
||||
selectedQuestion: null,
|
||||
answerMeaning: {
|
||||
userSupportedMeaning:
|
||||
"Avoiding additional risk is a strong preference rather than a hard constraint, with willingness to accept some risk if the opportunity is sufficiently compelling.",
|
||||
possibleInference:
|
||||
"The exact threshold for a sufficiently compelling opportunity remains undefined.",
|
||||
supportCategory: "conditional_preference",
|
||||
resolutionGuidance:
|
||||
"Identify and quantify the threshold conditions that trigger risk acceptance.",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.stage).toBe("proposal_compatibility");
|
||||
expect(result.errors.join(" ")).toContain("conditional qualification");
|
||||
expect(result.errors.join(" ")).not.toContain(
|
||||
"explicitly stated hard constraint",
|
||||
);
|
||||
});
|
||||
|
||||
it("B live-variant 2: default preference plus override stays conditional rather than other", () => {
|
||||
const { graph, riskUnknownId } = makeRiskClarificationFixture();
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
answer:
|
||||
"I'd normally avoid more risk, but for the right opportunity I might accept some.",
|
||||
previousQuestion:
|
||||
"Is avoiding additional risk a hard constraint or a preference/trade-off?",
|
||||
proposal: {
|
||||
addedNodes: [],
|
||||
updatedNodes: [
|
||||
{
|
||||
nodeId: riskUnknownId,
|
||||
previousStatus: "unknown",
|
||||
newStatus: "resolved",
|
||||
previousValue: null,
|
||||
newValue:
|
||||
"Avoiding additional risk is a preference or trade-off rather than a hard constraint.",
|
||||
reason:
|
||||
"The answer shows a preference or trade-off rather than a hard constraint.",
|
||||
},
|
||||
],
|
||||
addedEdges: [],
|
||||
removedEdgeIds: [],
|
||||
resolvedUnknownNodeIds: [riskUnknownId],
|
||||
affectedNodeIds: [],
|
||||
selectedQuestion: null,
|
||||
answerMeaning: {
|
||||
userSupportedMeaning:
|
||||
"Avoiding additional risk is a default preference, but it can be overridden for sufficiently compelling opportunities.",
|
||||
possibleInference: "The exact override threshold remains undefined.",
|
||||
supportCategory: "conditional_preference",
|
||||
resolutionGuidance: "needs more nuance",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.stage).toBe("proposal_compatibility");
|
||||
expect(result.errors.join(" ")).toContain("conditional qualification");
|
||||
expect(result.errors.join(" ")).not.toContain(
|
||||
"does not clearly establish one of the protected reasoning categories",
|
||||
);
|
||||
});
|
||||
|
||||
it("Regression C: rejects unresolved uncertainty being treated as resolved", () => {
|
||||
const { graph, riskUnknownId } = makeRiskClarificationFixture();
|
||||
|
||||
@@ -1341,6 +1435,52 @@ describe("applyValidatedProposal", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("negation safety: mentioning hard constraint in a negated comparison does not become affirmative hard constraint", () => {
|
||||
const { graph, riskUnknownId } = makeRiskClarificationFixture();
|
||||
|
||||
const result = applyValidatedProposal({
|
||||
situationGraph: graph,
|
||||
answer:
|
||||
"I'd normally avoid more risk, but for the right opportunity I might accept some.",
|
||||
previousQuestion:
|
||||
"Is avoiding additional risk a hard constraint or a preference/trade-off?",
|
||||
proposal: {
|
||||
addedNodes: [],
|
||||
updatedNodes: [
|
||||
{
|
||||
nodeId: riskUnknownId,
|
||||
previousStatus: "unknown",
|
||||
newStatus: "resolved",
|
||||
previousValue: null,
|
||||
newValue:
|
||||
"Avoiding additional risk is a preference or trade-off rather than a hard constraint.",
|
||||
reason:
|
||||
"The answer shows a preference or trade-off rather than a hard constraint.",
|
||||
},
|
||||
],
|
||||
addedEdges: [],
|
||||
removedEdgeIds: [],
|
||||
resolvedUnknownNodeIds: [riskUnknownId],
|
||||
affectedNodeIds: [],
|
||||
selectedQuestion: null,
|
||||
answerMeaning: {
|
||||
userSupportedMeaning:
|
||||
"Avoiding additional risk is a strong preference rather than a hard constraint.",
|
||||
possibleInference:
|
||||
"This indicates flexibility rather than an absolute prohibition.",
|
||||
supportCategory: "conditional_preference",
|
||||
resolutionGuidance: "needs more nuance",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.stage).toBe("proposal_compatibility");
|
||||
expect(result.errors.join(" ")).not.toContain(
|
||||
"weakens an explicitly stated hard constraint",
|
||||
);
|
||||
});
|
||||
|
||||
it("fails safely when answerMeaning does not clearly establish one of the protected categories", () => {
|
||||
const { graph, riskUnknownId } = makeRiskClarificationFixture();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user