(input: ClassifyInput)
| 110 | * one-way nature is found. Errs conservatively otherwise. |
| 111 | */ |
| 112 | export function classifyQuestion(input: ClassifyInput): ClassifyResult { |
| 113 | // 1. Registry lookup (primary) |
| 114 | if (input.question_id) { |
| 115 | const registered = getQuestion(input.question_id); |
| 116 | if (registered) { |
| 117 | return { |
| 118 | oneWay: registered.door_type === 'one-way', |
| 119 | reason: 'registry', |
| 120 | }; |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | // 2. Skill-category fallback (certain combos are always one-way) |
| 125 | if (input.skill && input.category) { |
| 126 | const key = `${input.skill}:${input.category}`; |
| 127 | if (ONE_WAY_SKILL_CATEGORIES.has(key)) { |
| 128 | return { oneWay: true, reason: 'skill-category' }; |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | // 3. Keyword pattern match (catch destructive questions without registry entry) |
| 133 | if (input.summary) { |
| 134 | for (const pattern of DESTRUCTIVE_PATTERNS) { |
| 135 | if (pattern.test(input.summary)) { |
| 136 | return { |
| 137 | oneWay: true, |
| 138 | reason: 'keyword', |
| 139 | matched: pattern.toString(), |
| 140 | }; |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | // 4. No evidence either way — treat as two-way (can be preference-suppressed). |
| 146 | return { oneWay: false, reason: 'default-two-way' }; |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Convenience wrapper for the sensitivity check binary. |
no test coverage detected