fix: preserve cognitive deferral in planning

This commit is contained in:
m
2026-08-21 01:21:00 +03:00
parent ed67fd7ba9
commit 73935b3f94
2 changed files with 24 additions and 15 deletions
+2 -2
View File
@@ -82,6 +82,7 @@ func DetermineHelpPath(input work.PlanningInput) work.HelpPath {
hasQueryLogsHint := hasPlanningHint(input, capability.CapabilityKindQueryLogs)
hasVerifyHint := hasPlanningHint(input, capability.CapabilityKindVerifyFact)
rawTextFallbackAllowed := len(input.CapabilityHints) == 0 && !input.RequiresClarification && !input.NeedsCognition
unsupportedCapabilityFallbackAllowed := len(input.CapabilityHints) == 0 && !input.RequiresClarification && !input.NeedsCognition
if input.SpeechAct != work.SpeechActRef("") && input.SpeechAct != work.SpeechActRef("UNKNOWN") {
rawTextFallbackAllowed = false
}
@@ -100,7 +101,7 @@ func DetermineHelpPath(input work.PlanningInput) work.HelpPath {
return work.HelpPath{Kind: work.HelpPathCapabilityGap, SuggestedCapabilityKinds: []capability.CapabilityKind{capability.CapabilityKindSendEmail}, CapabilityGap: true, Reason: "email sending needs a communication capability implementation and explicit authority", Reply: "Сейчас у меня ещё не подключена возможность отправлять почту. Я зафиксирую этот capability gap, чтобы позже к нему можно было вернуться."}
case hasModifyHint || hasInspectHint || hasQueryLogsHint || hasReadEmailHint || hasRecallHint || hasVerifyHint || needsDurableWorkFromHints(input):
return work.HelpPath{Kind: work.HelpPathDurableWork, NeedsDurableGoal: true, NeedsDurableWork: true, NeedsDurablePlan: true, Reason: "semantic hints indicate project/code work even if cognition may still be useful upstream"}
case isUnsupportedCapabilityRequest(lower):
case unsupportedCapabilityFallbackAllowed && isUnsupportedCapabilityRequest(lower):
return work.HelpPath{Kind: work.HelpPathCapabilityGap, SuggestedCapabilityKinds: []capability.CapabilityKind{}, CapabilityGap: true, Reason: "requested help needs a currently missing capability and may later trigger acquisition/self-extension", Reply: "Похоже, для этой просьбы нужна capability, которой сейчас нет. Это не задача конкретного executor автоматически: в будущем Домовой сможет найти, подключить или создать подходящую capability и вернуться к исходной потребности."}
case rawTextFallbackAllowed && requiresDurableWork(lower):
return work.HelpPath{Kind: work.HelpPathDurableWork, NeedsDurableGoal: true, NeedsDurableWork: true, NeedsDurablePlan: true, Reason: "request needs durable coordination, planning, investigation, or outcome tracking"}
@@ -144,7 +145,6 @@ func needsCognitiveAnswer(input work.PlanningInput) bool {
return containsAny(lower, "модель", "gemma4", "qwen", "спроси", "обратись", "посчитай")
}
func ValidatePlanCandidate(candidate work.PlanCandidate) error {
if candidate.ID == "" || candidate.GoalID == "" || candidate.Source == "" || candidate.Summary == "" || len(candidate.Steps) == 0 {
return ErrInvalidPlanCandidate
+22 -13
View File
@@ -109,20 +109,20 @@ func TestHelpPathComplexWorkMaterializesDurablePlanning(t *testing.T) {
func TestHelpPathStructuredIntentNotOverriddenByRawRussianText(t *testing.T) {
path := DetermineHelpPath(work.PlanningInput{
ConversationID: "conversation-1",
UtteranceID: "utterance-1",
IntentID: "intent-1",
ActorID: identity.ActorID("actor-1"),
PrincipalID: identity.PrincipalID("principal-1"),
ProjectID: work.ProjectRef("upo"),
ProjectDisplayName: "УПО",
ProjectProvenance: work.ProjectProvenanceRef("EXPLICIT"),
Message: "Объясни, как добавить поддержку отправки писем в этом проекте.",
SpeechAct: work.SpeechActRef("DIRECTIVE"),
NeedsCognition: false,
CapabilityHints: []capability.CapabilityKind{capability.CapabilityKindModifyCode},
ConversationID: "conversation-1",
UtteranceID: "utterance-1",
IntentID: "intent-1",
ActorID: identity.ActorID("actor-1"),
PrincipalID: identity.PrincipalID("principal-1"),
ProjectID: work.ProjectRef("upo"),
ProjectDisplayName: "УПО",
ProjectProvenance: work.ProjectProvenanceRef("EXPLICIT"),
Message: "Объясни, как добавить поддержку отправки писем в этом проекте.",
SpeechAct: work.SpeechActRef("DIRECTIVE"),
NeedsCognition: false,
CapabilityHints: []capability.CapabilityKind{capability.CapabilityKindModifyCode},
AssertionVerification: epistemics.VerificationNotApplicable,
ReceivedAtUnixMillis: time.Unix(100, 0).UnixMilli(),
ReceivedAtUnixMillis: time.Unix(100, 0).UnixMilli(),
})
if path.Kind != work.HelpPathDurableWork || !path.NeedsDurableWork || !path.NeedsDurablePlan {
t.Fatalf("structured durable intent should be preserved over raw phrase heuristics: %#v", path)
@@ -152,6 +152,15 @@ func TestHelpPathStructuredHintsOverrideRawCognitionPhrase(t *testing.T) {
}
}
func TestHelpPathDoesNotUseUnsupportedCapabilityHeuristicAfterCognitiveDeferral(t *testing.T) {
input := planningInputForTest("Подключи интеграцию с неизвестным сервисом, которого сейчас нет.", "DIRECTIVE", epistemics.VerificationNotApplicable)
input.NeedsCognition = true
path := DetermineHelpPath(input)
if path.Kind == work.HelpPathCapabilityGap {
t.Fatalf("raw unsupported-capability phrase must not override deferred cognitive interpretation: %#v", path)
}
}
func planForTest(t *testing.T, message string, act string, verification epistemics.VerificationState) work.PlanningResult {
t.Helper()
return planForTestWithInput(t, planningInputForTest(message, act, verification))