package workflows import ( "context" "errors" "strings" ) var ErrNotImplemented = errors.New("workflow runner not implemented") type DiagnosticInput struct { UserID string Track string TargetRole string Stack []string } type Runner interface { DiagnoseJobSeeker(context.Context, DiagnosticInput) (DiagnosticResult, error) GradeInterviewAnswer(context.Context, GradeAnswerInput) (GradedAnswer, error) ExtractLearningMemory(context.Context, GradedAnswer) (MemoryUpdateCandidate, error) SelectNextChallenge(context.Context, NextChallengeInput) (NextChallenge, error) UpdateReadinessMap(context.Context, ReadinessUpdateInput) (ReadinessUpdate, error) } type GradeAnswerInput struct { UserID string QuestionID string AnswerID string AnswerText string Concepts []ConceptRef } type NextChallengeInput struct { UserID string Track string } type ReadinessUpdateInput struct { UserID string Track string } type StubRunner struct{} func NewStubRunner() StubRunner { return StubRunner{} } func (StubRunner) DiagnoseJobSeeker(context.Context, DiagnosticInput) (DiagnosticResult, error) { return DiagnosticResult{}, ErrNotImplemented } func (StubRunner) GradeInterviewAnswer(_ context.Context, input GradeAnswerInput) (GradedAnswer, error) { wordCount := len(strings.Fields(input.AnswerText)) overall := AnswerPartial if wordCount >= 18 { overall = AnswerSolid } if wordCount < 8 { overall = AnswerMiss } grade := GradedAnswer{ UserID: input.UserID, AnswerID: input.AnswerID, QuestionID: input.QuestionID, Concepts: append([]ConceptRef(nil), input.Concepts...), Scores: AnswerScores{ Correctness: scoreFromWords(wordCount, 8), Depth: scoreFromWords(wordCount, 14), Communication: scoreFromWords(wordCount, 10), ProductionJudgment: scoreFromWords(wordCount, 20), }, Overall: overall, Strengths: []string{"Answer was captured and evaluated through the typed workflow boundary."}, Gaps: []string{}, Evidence: []EvidenceRef{ { Kind: EvidenceAnswer, ID: input.AnswerID, Quote: input.AnswerText, Confidence: 1, }, }, FollowUp: FollowUpRecommendation{}, } if overall == AnswerMiss || overall == AnswerPartial { grade.Gaps = []string{"Answer needs more concrete reasoning and tradeoff discussion."} grade.FollowUp = FollowUpRecommendation{ Needed: true, Question: "Can you give a concrete production example and explain the tradeoff?", Purpose: FollowUpRepair, } } return grade, nil } func (StubRunner) ExtractLearningMemory(_ context.Context, grade GradedAnswer) (MemoryUpdateCandidate, error) { candidate := MemoryUpdateCandidate{ UserID: grade.UserID, SourceAnswerID: grade.AnswerID, Updates: []MemoryUpdate{}, } if len(grade.Evidence) == 0 { return candidate, nil } state := readinessFromOverall(grade.Overall) durability := DurabilityTentative if grade.Overall == AnswerStrong { durability = DurabilityConfirmed } for _, concept := range grade.Concepts { candidate.Updates = append(candidate.Updates, MemoryUpdate{ Kind: MemoryConceptMastery, Concept: concept, ProposedState: state, Summary: "Concept readiness updated from diagnostic interview answer.", Evidence: append([]EvidenceRef(nil), grade.Evidence...), Confidence: confidenceFromOverall(grade.Overall), Durability: durability, }) if grade.FollowUp.Needed { candidate.Updates = append(candidate.Updates, MemoryUpdate{ Kind: MemoryMisconception, Concept: concept, ProposedState: ReadinessFragile, Summary: "Needs more concrete reasoning and tradeoff discussion.", Evidence: append([]EvidenceRef(nil), grade.Evidence...), Confidence: 0.62, Durability: DurabilityTentative, }, MemoryUpdate{ Kind: MemoryIntervention, Concept: concept, ProposedState: state, Summary: grade.FollowUp.Question, Evidence: append([]EvidenceRef(nil), grade.Evidence...), Confidence: 0.7, Durability: DurabilityTentative, }, MemoryUpdate{ Kind: MemoryReviewSchedule, Concept: concept, ProposedState: state, Summary: "Review with a concrete production example before raising difficulty.", Evidence: append([]EvidenceRef(nil), grade.Evidence...), Confidence: 0.7, Durability: DurabilityTentative, }, ) } } return candidate, nil } func (StubRunner) SelectNextChallenge(context.Context, NextChallengeInput) (NextChallenge, error) { return NextChallenge{}, ErrNotImplemented } func (StubRunner) UpdateReadinessMap(context.Context, ReadinessUpdateInput) (ReadinessUpdate, error) { return ReadinessUpdate{}, ErrNotImplemented } func scoreFromWords(wordCount int, target int) int { if wordCount >= target { return 4 } if wordCount >= target/2 { return 2 } return 1 } func readinessFromOverall(overall AnswerOverall) ReadinessState { switch overall { case AnswerMiss: return ReadinessFragile case AnswerPartial: return ReadinessImproving case AnswerSolid: return ReadinessInterviewReady case AnswerStrong: return ReadinessStrongSignal default: return ReadinessUnknown } } func confidenceFromOverall(overall AnswerOverall) float64 { switch overall { case AnswerMiss: return 0.58 case AnswerPartial: return 0.68 case AnswerSolid: return 0.82 case AnswerStrong: return 0.9 default: return 0.5 } }