Pure moves only — zero behavior change, template untouched:
- types.ts (447), utils.ts (51), data/{auth,navigation,practice,mentor,
home,supervisor,icons}.ts, styles/app.css (4690, still global non-scoped
via <style src>)
- App.vue 9006 → 3559 lines; 98 moved declarations verbatim except
export/import syntax
- Built CSS hash identical to pre-refactor (byte-equal styles); reactive
state, computed, watchers, lifecycle and templates all stay in App.vue
- Component-level split deferred to phase two after 2026-07-10 Alpha
- New aihr_knowledge_answer_feedback table; unique (tenant, query_norm,
fragment_id) pair keeps submissions idempotent
- POST /api/knowledge/answer-feedback (fragmentIds array), GET pending-review
list, POST /{id}/review — all under existing auth scope
- Down-voted (question, fragment) pairs are filtered from dbSearch fusion
results; if filtering empties the hit list the original results are kept
with an explanatory caution (never degrade to no-answer)
- Feedback table missing/unreachable → filter silently skipped, search
behavior unchanged (degradation chain preserved)
- SnippetResponse gains optional trailing fragmentId (db hits only; seed
fallback stays null); mobile sopPanel adds 👍/👎 with locked state after
submit, up votes auto-marked reviewed to keep the review queue clean
- Seed aihr_prompt_template with code=sop_answer (conclusion-first + numbered
steps + citations appended), matching the built-in prompt
- Digest prefers the enabled sop_answer template, falls back to the built-in
constant, and logs prompt_version; answer/keyPoints/cautions contract unchanged