Lewati ke isi

FE-BE CBT Question, Exam, and Bank Soal Sync Recon

  • Tanggal recon: 2026-05-27, Asia/Jakarta
  • Mode kerja: local repo recon, docs-only
  • Frontend root: /Users/salfath/scola-fe-v2
  • Backend root: /Users/salfath/scola-odoo-module

Dokumen ini merangkum hasil recon mendalam atas konsistensi implementasi frontend dan backend untuk area:

  • Bank soal / question bank
  • Pembuatan dan pengelolaan ujian CBT
  • Runner pengerjaan ujian
  • Peserta, proctoring, grading, hasil, analytics
  • Context learning, independent, dan admission
  • Jembatan SPMB CBT
  • Feature flag, capability, dan access control

1. Dokumen Tata Kelola Yang Dibaca

Sesuai instruksi AGENTS.md, recon dimulai dengan membaca dokumen aktif berikut:

  • docs/ai-guidelines/development-guide.md
  • docs/ai-guidelines/workspace-governance.md
  • docs/ai-guidelines/architecture-api.md
  • docs/qa/testing-guidelines.md

Catatan tata kelola:

  • Frontend repo adalah SSOT dokumentasi UI/domain untuk perubahan ini.
  • Backend repo yang direkon adalah /Users/salfath/scola-odoo-module.
  • Tidak ada perubahan kode aplikasi dalam recon ini.
  • Dokumentasi baru ditempatkan di path frontend yang diizinkan: docs/domains/cbt/.
  • Arsitektur same-origin API tetap konsisten. Service frontend memanggil namespace /scola_cbt/... melalui apiClient yang memiliki base /api, sehingga endpoint efektif menjadi /api/scola_cbt/....

2. Ringkasan Eksekutif

Implementasi FE-BE untuk CBT sudah cukup sinkron di jalur utama:

  • Namespace endpoint FE dan BE konsisten untuk question_set, question, exam, runner, grading, proctor, results, analytics, export, policy, dan audit.
  • Model backend scola.cbt.question.set, scola.cbt.question.item, scola.cbt.question.choice, scola.cbt.question.asset, op.exam, scola.cbt.participant, scola.cbt.attempt, dan scola.cbt.attempt.answer selaras dengan payload utama yang dikirim FE.
  • Workflow learning, independent, dan admission sudah dipisahkan di FE dan BE.
  • Jalur SPMB CBT sudah terhubung: FE memilih cbt_exam_id context admission pada jadwal SPMB, BE memastikan exam benar-benar CBT admission dan membuat/sinkron participant serta nilai.
  • Access control mayoritas sinkron: FE memakai capability/feature flag, BE memakai Odoo group, assigned staff, ownership, faculty scope, dan admission context manager.

Temuan utama yang perlu ditindaklanjuti bukan pada route dasar, tetapi pada semantik fitur:

  • Beberapa field policy muncul dan bisa disimpan dari FE, tetapi belum dipakai penuh di runtime BE: grace_period_minutes, block_multi_login, allow_negative_marking, negative_mark_percentage, dan show_score_immediately.
  • Import soal mendukung tipe mcq_multi, tetapi format CSV FE/BE saat ini hanya menerima satu correct_answer A-E, sehingga multi-correct import belum benar-benar tersedia.
  • Public CBT route FE tidak memiliki featureFlag: "scola_cbt", berbeda dengan route CBT authenticated.
  • Route detail pendaftar SPMB memasang featureFlag: "scola_cbt" pada parent detail admission, sehingga bagian non-CBT detail pendaftar berisiko ikut tertutup pada tenant tanpa CBT.

3. Scope File Utama

3.1 Frontend

Service:

  • src/api/apiServices.js
  • src/services/cbt/questionService.js
  • src/services/cbt/examService.js
  • src/services/cbt/cbtRunnerService.js
  • src/services/cbt/gradingService.js
  • src/services/cbt/proctorService.js
  • src/services/cbt/examResults.service.js
  • src/services/cbt/cbtService.js
  • src/services/spmb/spmbAdminTestSchedule.service.js

Views dan composables:

  • src/views/ExamManagement/Faculty/QuestionBank/CBTQuestionList.vue
  • src/views/ExamManagement/Faculty/QuestionBank/CBTQuestionSetDetail.vue
  • src/views/ExamManagement/Faculty/QuestionBank/CBTQuestionForm.vue
  • src/views/ExamManagement/Faculty/Exam/CBTExamList.vue
  • src/views/ExamManagement/Faculty/Exam/CBTExamDetail.vue
  • src/views/AssignmentManagement/Faculty/AddExamPage.vue
  • src/views/AssignmentManagement/Faculty/components/EditExamModal.vue
  • src/views/ExamManagement/Student/CBTRunner.vue
  • src/composables/cbt/useExamState.js
  • src/views/AdmissionManagement/TestSchedule.vue
  • src/views/AdminViews/CBT/CBTSettings.vue

Routes, access, and tests:

  • src/router/teacherRouteFragments/assessment.js
  • src/router/studentRouteFragments/learning.js
  • src/router/spmbRoutes.js
  • src/router/admissionsRoutes.js
  • src/router/sharedExperienceRoutes.js
  • src/config/capabilityFragments/learningAssessmentCapabilities.js
  • src/config/capabilityFragments/admissionsCapabilities.js
  • src/config/roleFragments/operationalRoles.js
  • src/config/roleFragments/portalRoles.js
  • src/access/featureFlagFragments/learningAssessmentAccess.js
  • tests/menu/cbtMenuRouteRbacSync.spec.js
  • tests/menu/teacherCbtFeatureFlag.spec.js
  • tests/menu/teacherCapabilityCoverage.spec.js
  • tests/menu/routerFeatureFlagGuard.spec.js

3.2 Backend

CBT module:

  • scola_cbt/controllers/question_set_api.py
  • scola_cbt/controllers/question_api.py
  • scola_cbt/controllers/exam.py
  • scola_cbt/controllers/exam_read_api.py
  • scola_cbt/controllers/exam_lifecycle_api.py
  • scola_cbt/controllers/exam_attendee_api.py
  • scola_cbt/controllers/participant.py
  • scola_cbt/controllers/exam_lookup_api.py
  • scola_cbt/controllers/exam_staff_api.py
  • scola_cbt/controllers/runner_start_api.py
  • scola_cbt/controllers/runner_attempt_api.py
  • scola_cbt/controllers/runner_integrity_api.py
  • scola_cbt/controllers/grading_read_api.py
  • scola_cbt/controllers/grading_write_api.py
  • scola_cbt/controllers/proctor_read_api.py
  • scola_cbt/controllers/proctor_action_api.py
  • scola_cbt/controllers/report_results_api.py
  • scola_cbt/controllers/report_analytics_api.py
  • scola_cbt/controllers/report_export_api.py
  • scola_cbt/controllers/main.py

Backend models/security:

  • scola_cbt/models/question_set.py
  • scola_cbt/models/question.py
  • scola_cbt/models/exam.py
  • scola_cbt/models/participant.py
  • scola_cbt/models/attempt.py
  • scola_cbt/models/policy.py
  • scola_cbt/models/assessment_grade.py
  • scola_cbt/models/item_analysis.py
  • scola_cbt/security/ir.model.access.csv
  • scola_cbt/security/cbt_security.xml
  • scola_cbt/security/question_set_rules.xml
  • scola_cbt/security/spmb_cbt_bridge.xml

SPMB bridge:

  • scola_admission_assessment/models/admission_test.py
  • scola_admission_assessment/models/cbt_participant.py
  • scola_admission_assessment/controllers/spmb_assessment_schedules_api.py
  • scola_admission/controllers/spmb_complaints_ranking_api.py

4. API Architecture Sync

4.1 Same-Origin Contract

Frontend service memanggil endpoint seperti:

  • /scola_cbt/question_set/list
  • /scola_cbt/exams/create
  • /scola_cbt/runner/start

Karena apiClient memakai base URL /api, request efektif menjadi:

  • /api/scola_cbt/question_set/list
  • /api/scola_cbt/exams/create
  • /api/scola_cbt/runner/start

Backend controller mendeklarasikan route dengan prefix /api/scola_cbt/.... Ini konsisten dengan docs/ai-guidelines/architecture-api.md.

Export XLSX adalah pengecualian yang memang memakai URL GET absolut:

  • FE: /api/scola_cbt/export/exam/:id/xlsx
  • BE: /api/scola_cbt/export/exam/<id>/xlsx
  • FE: /api/scola_cbt/export/integrity/:id/xlsx
  • BE: /api/scola_cbt/export/integrity/<id>/xlsx

4.2 JSON-RPC Shape

Service FE umumnya mengirim:

{
  "jsonrpc": "2.0",
  "method": "call",
  "params": {}
}

Backend route bertipe type='json' dan membaca argumen langsung dari params. Bentuk ini sinkron.

5. Endpoint Contract Matrix

5.1 Bank Soal: Question Set

FE service BE route Status Catatan
listQuestionSets(filters, offset, limit) /api/scola_cbt/question_set/list OK Filter subject_id, search, offset, limit sinkron.
getQuestionSet(id) /api/scola_cbt/question_set/get OK BE return question_set; FE mengonsumsi detail set.
createQuestionSet(values) /api/scola_cbt/question_set/create OK name, description, subject_id sinkron.
updateQuestionSet(id, values) /api/scola_cbt/question_set/update OK BE batasi manager atau owner set.
deleteQuestionSet(id) /api/scola_cbt/question_set/delete OK BE block jika set dipakai exam active/non-cancel.

Catatan:

  • FE sorting question set dilakukan lokal atas halaman data yang sedang tampil. BE menerima argumen order, tetapi wrapper FE belum mengeksposnya. Jika sorting di UI dimaksudkan global lintas halaman, kontrak belum lengkap.

5.2 Bank Soal: Question Item, Choice, Asset, Tag

FE service BE route Status Catatan
listQuestions(setId) /api/scola_cbt/question/list OK FE kirim question_set_id; BE return item, choices, assets, tags.
getQuestion(id) /api/scola_cbt/question/get OK Dipakai form edit.
createQuestion(questionSetId, values) /api/scola_cbt/question/create OK question_set_id, question_type, content, explanation, difficulty, points, tag_ids, choices, assets.
updateQuestion(id, values) /api/scola_cbt/question/update OK BE versioning saat soal yang sudah dipakai exam diedit.
deleteQuestion(id) /api/scola_cbt/question/delete Partial Endpoint ada, tetapi BE membatasi ke CBT Manager; FE utama tidak mengekspos aksi delete item.
importQuestions(setId, fileContent, fileName) /api/scola_cbt/question/import Partial CSV import OK untuk single answer; multi-correct belum lengkap.
listSubjects() /api/scola_cbt/subject/list OK Subject lookup untuk question set/form.
listTags(subjectId) /api/scola_cbt/tag/list OK BE saat ini lebih permisif: auth user dapat list tag.
createTag(name, colorIndex, subjectId) /api/scola_cbt/tag/create OK Dipakai FE untuk tag baru dari form soal.

Tipe soal yang sinkron:

  • mcq
  • mcq_multi
  • true_false
  • short_answer
  • essay

Kontrak asset sinkron:

  • asset_type: image, audio, video, document
  • Sumber asset: url atau file_content + file_name
  • Existing asset dapat dipertahankan via id dan metadata

Gap import multi-correct:

  • FE form manual mendukung mcq_multi dengan banyak pilihan benar.
  • Runner dan BE scoring mendukung mcq_multi dengan partial credit.
  • Tetapi import CSV FE/BE memakai kolom correct_answer tunggal A-E.
  • Backend import juga memvalidasi correct_answer sebagai satu huruf A-E.
  • Dampak: mcq_multi hasil import hanya dapat memiliki satu jawaban benar, walaupun tipe soal mendukung multi answer.

5.3 Exam Management

FE service/view BE route/model Status Catatan
listExams(filters, offset, limit) /api/scola_cbt/exams OK Filter exam_context, exclude_learning, subject_id, state, search sinkron.
getExam(id) /api/scola_cbt/exams/<id> OK Return includes staff flags: can_manage_staff, can_proctor, can_grade.
createExam(values) /api/scola_cbt/exams/create OK question_set_id menentukan is_cbt; learning CBT wajib grade_component_type.
updateExam(id, values) /api/scola_cbt/exams/<id>/update OK Field CBT, timing, marks, remedial, grade mapping sinkron.
deleteExam(id) /api/scola_cbt/exams/<id>/delete OK BE hanya izinkan draft/cancel.
transitionExamState(id, state) /api/scola_cbt/exams/<id>/transition OK FE state action cocok dengan BE transition.
regenerateToken(id) /api/scola_cbt/exams/<id>/regenerate-token OK FE update token dari response.
listExamTypes() /api/scola_cbt/lookup/exam-types OK Lookup exam type.
listExamBatches() /api/scola_cbt/lookup/batches OK Non-learning form menjadikan batch optional; BE auto-create subject/batch/session untuk non-learning bila perlu.
listCbtStudents() /api/scola_cbt/lookup/students OK Dipakai attendee/participant manual.
listCbtStaffUsers() /api/scola_cbt/lookup/staff-users OK BE requires create/manage access.
updateExamStaff() /api/scola_cbt/exams/<id>/staff/update OK BE juga memberi Odoo group ke assigned author/proctor/grader.

5.4 Learning Exam Flow

Frontend learning CBT dibuat dari:

  • src/views/AssignmentManagement/Faculty/AddExamPage.vue
  • src/views/AssignmentManagement/Faculty/components/EditExamModal.vue

Contract:

  • FE wajib memilih course/batch/subject.
  • FE mengirim exam_context: "learning".
  • FE mengirim question_set_id untuk mengaktifkan CBT.
  • FE mewajibkan grade_component_type ketika question_set_id dipilih.
  • BE juga menolak learning CBT tanpa grade_component_type.
  • BE membuat attendees dari batch dan menyinkron nilai ke op.exam.attendees.
  • BE dapat publish ke academic.grade untuk learning CBT yang punya mapping komponen nilai.

Status: sinkron.

5.5 Independent and Admission Exam Flow

Frontend non-learning/admission CBT dibuat dari:

  • src/views/ExamManagement/Faculty/Exam/CBTExamList.vue
  • Reused oleh admin/admin_staff SPMB routes via route meta examContext: "admission"

Contract:

  • Default non-forced context adalah independent.
  • Route SPMB internal memaksa exam_context: "admission".
  • FE mewajibkan question_set_id.
  • BE membuat exam non-learning dengan helper subject/batch/session jika field learning tidak tersedia.
  • Participant manual/import hanya benar-benar berlaku untuk independent.
  • Admission participant dikelola dari SPMB schedule bridge, bukan dari manual participant import.

Status: sinkron.

5.6 Attendees and Participants

Context FE behavior BE behavior Status
learning List/add/remove attendee siswa internal. op.exam.attendees; transition schedule/held memastikan peserta learning. OK
independent Manual participant create/import/state/remove. scola.cbt.participant dengan identity student atau external; public runner butuh participant code. OK
admission Participant berasal dari jadwal SPMB; manual remove/import dicegah UI dan BE. scola_admission_assessment membuat participant identity admission dari slot pendaftar. OK

5.7 Runner

FE service BE route Status Catatan
startAttempt(examId, token, { participantCode }) /api/scola_cbt/runner/start OK Public only independent; authenticated admission dapat start tanpa token.
getAttemptState(attemptId) /api/scola_cbt/runner/state OK FE polling untuk force-submit/proctor state.
saveAnswer(attemptId, questionId, answerData) /api/scola_cbt/runner/answer OK Single answer autosave.
syncAnswers(attemptId, answers) /api/scola_cbt/runner/sync OK Bulk sync dirty answers.
submitAttempt(attemptId) /api/scola_cbt/runner/submit OK BE auto-score objective questions.
logIntegrityEvents(attemptId, events) /api/scola_cbt/runner/integrity OK FE event names match backend accepted values.

Answer payload sync:

  • Single choice / true-false: answer_option_ids: [choiceId]
  • Multi choice: answer_option_ids: [choiceId, ...]
  • Essay / short answer: answer_char: string

Timer contract:

  • BE calculates attempt deadline from cbt_duration, capped by exam.end_time.
  • FE countdown is hydrated from remaining_seconds.
  • FE periodically refreshes state from BE.
  • BE auto-submits on timeout during save/sync.

5.8 Grading

FE service BE route Status Catatan
getAttemptForGrading(attemptId) /api/scola_cbt/grading/attempt/<id> OK Includes question, answers, assets, choices, rubric/revision data.
scoreAnswer(payload) /api/scola_cbt/grading/score OK attempt_id, answer_id, score, comment, reason.
finalizeGrading(attemptId) /api/scola_cbt/grading/finalize/<id> OK Attempt moves to graded; score sync downstream.

BE grading access permits:

  • CBT manager
  • CBT grader
  • assigned grader
  • admission context manager
  • faculty managing the exam

FE teacher grading route uses academics.cbt_exams.manage, not academics.cbt_grading.manage. This matches existing test expectation that teacher-managed exams remain reachable, but leaves academics.cbt_grading.manage underused for academic teacher routes.

5.9 Proctor

FE service BE route Status Catatan
listProctorExams() /api/scola_cbt/proctor/exams OK Access via assigned proctor/manager/admission manager.
getProctorExamStatus(id) /api/scola_cbt/proctor/exam/<id>/status OK Monitoring attempt status.
getAttemptFlags(id) /api/scola_cbt/proctor/attempt/<id>/flags OK Integrity flags.
extendTime(id, minutes, reason) /api/scola_cbt/proctor/attempt/<id>/extend_time OK Extends attempt deadline.
forceSubmit(id, reason) /api/scola_cbt/proctor/attempt/<id>/force_submit OK FE runner detects submitted state.
resetAttempt(id, reason) /api/scola_cbt/proctor/attempt/<id>/reset OK Reset attempt for retake.

Important sync point:

  • FE generic teacher role intentionally cannot open /faculty/cbt/proctor.
  • BE also does not allow generic teacher to proctor unless assigned proctor or privileged group.
  • This is enforced by FE test teacherCapabilityCoverage.spec.js.

5.10 Results, Analytics, Export

FE service BE route Status Catatan
getExamResults(examId) /api/scola_cbt/results/exam/<id> OK FE normalizes rows/statistics/pipeline.
publishExamResults(examId) /api/scola_cbt/results/exam/<id>/publish OK Learning gradebook path only.
getExamAnalytics(examId) /api/scola_cbt/analytics/exam/<id> OK Score distribution.
getIntegrityAnalytics(examId) /api/scola_cbt/analytics/integrity/<id> OK Integrity report.
getOperationalAnalytics(examId) /api/scola_cbt/analytics/operations/<id> OK Participant monitoring.
export exam XLSX /api/scola_cbt/export/exam/<id>/xlsx OK GET URL used directly.
export integrity XLSX /api/scola_cbt/export/integrity/<id>/xlsx OK GET URL used directly.

6. Data Model Sync

6.1 Question Set

BE model scola.cbt.question.set:

  • code
  • name
  • description
  • subject_id
  • question_ids
  • computed question_count
  • computed total_points
  • created_by
  • company_id

FE question set views consume the same fields for list/detail/cards.

Status: OK.

6.2 Question Item

BE model scola.cbt.question.item:

  • question_set_id
  • subject_id related from set
  • question_type
  • content
  • explanation
  • difficulty
  • points
  • tag_ids
  • choice_ids
  • asset_ids
  • rubric_data
  • versioning fields

FE question form sends and reads matching fields. Versioning is backend-owned and does not require FE-specific payload.

Status: OK.

6.3 Choice

BE model scola.cbt.question.choice:

  • question_id
  • content
  • is_correct
  • points_if_selected
  • constraint: mcq max one correct answer

FE form enforces:

  • objective question requires at least 2 options
  • objective question requires at least 1 correct option
  • mcq UI effectively serializes one correct answer
  • mcq_multi serializes multiple correct answers

Status: OK for manual authoring; partial for CSV import.

6.4 Asset

BE model scola.cbt.question.asset:

  • asset_type
  • name
  • url
  • file
  • file_name
  • mime_type
  • question relation

FE supports:

  • image, audio, video, document
  • URL source
  • uploaded file encoded as base64
  • preserving existing uploaded file on edit

Status: OK.

6.5 Exam

BE extends op.exam with CBT fields:

  • is_cbt
  • exam_context
  • question_set_id
  • cbt_duration
  • cbt_token
  • cbt_shuffle_questions
  • cbt_shuffle_options
  • cbt_show_result
  • cbt_author_user_ids
  • cbt_proctor_user_ids
  • cbt_grader_user_ids
  • remedial config
  • grade mapping: grade_component_type, grade_component_weight, auto_publish_to_gradebook

FE exam forms and detail views use these fields consistently.

Status: OK.

6.6 Participant and Attempt

BE participant supports:

  • learning: linked student participant
  • independent: internal student or external participant
  • admission: admission applicant participant from SPMB bridge

BE attempt supports:

  • attempt lifecycle: draft -> in_progress -> submitted -> graded
  • per-attempt public access token for independent external public runner
  • persistent question order
  • answer upsert
  • auto-scoring objective questions
  • manual grading for essay/short answer
  • score sync to op.exam.attendees or admission slot

FE runner and grading consume this contract correctly.

Status: OK.

7. Context Sync

7.1 Learning

Purpose:

  • Ulangan/ujian kelas internal.

FE entry points:

  • /faculty/assignments/exam-create
  • /faculty/exam/detail/:id
  • /student/cbt
  • /student/cbt/runner/:exam_id

BE contract:

  • exam_context = "learning"
  • batch_id and subject_id required
  • question_set_id makes exam CBT
  • learning CBT requires grade_component_type
  • participant source is op.exam.attendees
  • scores sync to op.exam.attendees
  • optional publish to academic.grade

Status: OK.

7.2 Independent

Purpose:

  • Tryout, olympiad, public/external CBT outside kelas reguler.

FE entry points:

  • /faculty/cbt/exams
  • /faculty/cbt/exams/:examId
  • /cbt/public
  • /cbt/public/runner/:exam_id

BE contract:

  • exam_context = "independent"
  • public runner only allowed for independent external participants
  • public runner requires participant_code
  • participant manual/import allowed
  • results report only, no academic grade publish

Status: mostly OK. Public route feature flag gap noted below.

7.3 Admission

Purpose:

  • Tes masuk SPMB.

FE entry points:

  • /admin/spmb/cbt/exams
  • /admin-staff/admissions/cbt/exams
  • /spmb/cbt
  • /spmb/cbt/runner/:exam_id
  • src/views/AdmissionManagement/TestSchedule.vue mapping cbt_exam_id

BE contract:

  • exam_context = "admission"
  • internal SPMB CBT exam list forced to admission context
  • SPMB schedule can only link CBT exam context admission
  • schedule sync creates or updates scola.cbt.participant identity admission
  • authenticated admission portal user can start runner without token
  • score sync updates scola.admission.test.slot

Status: OK.

8. Access and RBAC Sync

8.1 Frontend Capability and Feature Flag

Main FE capability mapping:

  • academics.question_bank.manage
  • academics.cbt_exams.manage
  • academics.cbt_exams.sit
  • academics.cbt_proctor.manage
  • academics.cbt_grading.manage
  • academics.exam_results.view
  • admissions.cbt_author.manage
  • admissions.cbt_proctor.manage
  • admissions.cbt_grade.manage

Main FE feature flag:

  • scola_cbt

Tests already cover several contracts:

  • CBT teacher routes carry scola_cbt.
  • Student and SPMB CBT routes carry scola_cbt.
  • Internal SPMB CBT routes are admission-scoped.
  • Generic teacher does not get proctor route.
  • Teacher grading route stays reachable via exam management capability.
  • Admin CBT settings is blocked when scola_cbt is disabled.

8.2 Backend Groups and Access

Relevant BE groups:

  • scola_cbt.group_cbt_manager
  • scola_cbt.group_cbt_exam_creator
  • scola_cbt.group_cbt_proctor
  • scola_cbt.group_cbt_grader
  • scola_cbt.group_cbt_auditor

Admission bridge:

  • scola_core.group_scola_admin_staff implies CBT exam creator and proctor.
  • scola_core.group_scola_head_admin implies CBT manager.

Controller-level access:

  • Question read/create: manager, exam creator, faculty-like user, admission manager for read.
  • Question set update/delete: manager or owner; delete blocked when active exam uses the set.
  • Exam create: faculty-like, CBT manager, CBT exam creator, OpenEduCat admin.
  • Existing exam manage: manager/admin, admission manager for admission exams, assigned author, owner/creator, faculty scope.
  • Proctor: manager/admin, assigned proctor, admission manager.
  • Grading: manager, grader, assigned grader, admission manager, faculty managing exam.

Status: mostly OK.

8.3 Access Notes

  1. Generic teacher can create and manage own learning CBT and question bank via FE capability and BE faculty-like/ownership checks.
  2. Generic teacher cannot proctor unless explicitly assigned or given proctor group. FE and BE align.
  3. SPMB admin/admin_staff routes use admissions CBT capabilities; BE group bridge makes these users eligible on CBT controllers.
  4. Academic grading route uses academics.cbt_exams.manage, while academics.cbt_grading.manage exists in registry but is not the teacher route gate. This is intentional per current test, but should be documented as route-policy decision.

9. Policy Sync

9.1 Fields Synced From Settings to Backend

FE CBTSettings.vue can read/update policy fields:

  • default_duration_minutes
  • grace_period_minutes
  • auto_submit_on_timeout
  • allow_back_navigation
  • allow_question_flag
  • show_question_navigator
  • flag_tab_switch
  • flag_blur_event
  • flag_paste_event
  • block_multi_login
  • fullscreen_required
  • max_flag_count
  • show_score_immediately
  • allow_negative_marking
  • negative_mark_percentage
  • max_attempts
  • retake_score_policy
  • autosave_interval_seconds
  • autosave_on_change
  • autosave_debounce_ms

BE scola.cbt.policy stores and returns those fields through /api/scola_cbt/policy/get and runner policy payload.

9.2 Fields Used by FE Runner

FE runner actively uses:

  • show_question_navigator
  • allow_question_flag
  • allow_back_navigation
  • fullscreen_required
  • flag_tab_switch
  • flag_blur_event
  • flag_paste_event
  • autosave_interval_seconds
  • autosave_on_change
  • autosave_debounce_ms

9.3 Fields Used by BE Runtime

BE runtime actively uses:

  • max_flag_count for integrity threshold.
  • cbt_duration from exam, not policy default, to calculate attempt deadline.
  • exam.max_attempts for retake count.
  • exam.cbt_show_result to decide whether submit response includes score.

9.4 Policy Gaps

The following policy fields are stored and exposed, but no complete runtime enforcement was found in the recon:

Field Observed state Risk
grace_period_minutes Stored and returned, but runner time gate/deadline uses strict exam.end_time and attempt end_time. Admin may expect grace period to allow late sync/start, but runtime does not honor it.
block_multi_login Stored and returned, but no active attempt/session blocking was found in runner start/state. Admin may enable multi-login blocking without effect.
allow_negative_marking Stored and returned. Scoring always gives zero for wrong single-choice answer and partial non-negative score for mcq_multi.
negative_mark_percentage Stored and returned. No deduction path observed in _auto_score.
show_score_immediately Stored and returned. Submit response uses exam.cbt_show_result, not policy show_score_immediately.

Recommendation:

  • Either implement enforcement in BE runtime, or hide/label these settings as "planned/not enforced" until implemented.
  • Prefer BE enforcement for anything affecting scoring, timing, and security.

10. SPMB CBT Bridge Sync

10.1 FE Flow

src/views/AdmissionManagement/TestSchedule.vue:

  • Loads CBT exams with filter { exam_context: "admission" }.
  • Lets admin map form.cbt_exam_id.
  • Persists mapping through /api/SPMB/test-schedules/<id>/update.
  • Runs sync through /api/SPMB/test-schedules/<id>/sync-cbt with mode participants, results, or both.

src/services/spmb/spmbAdminTestSchedule.service.js:

  • Normalizes cbt_exam_id, cbt_exam_context, cbt_participant_count, and cbt_last_synced_at.
  • Serializes cbt_exam_id on create/update.

10.2 BE Flow

scola_admission_assessment:

  • Adds cbt_exam_id to admission test schedule.
  • Domain and constraint require is_cbt = True and exam_context = "admission".
  • Creates or links scola.cbt.participant with identity_type = "admission".
  • Syncs submitted/graded attempt score back to admission test slot.

Status: OK.

10.3 Admission Runner

FE /spmb/cbt/runner/:exam_id reuses CBTRunner.vue with route meta cbtContext: "admission".

BE runner:

  • Allows authenticated admission context runner to start without token.
  • Resolves participant by user_id or admission_id.portal_user_id.
  • Rejects duplicate participant mapping for same user/exam.

Status: OK.

11. Findings and Recommendations

High

H1. CBT policy fields are configurable but not fully enforced

Affected fields:

  • grace_period_minutes
  • block_multi_login
  • allow_negative_marking
  • negative_mark_percentage
  • show_score_immediately

Impact:

  • Admin UI implies runtime behavior that may not happen.
  • Scoring and timing policy are high-trust features; mismatch can affect exam fairness.

Recommendation:

  • Implement BE enforcement and add BE tests.
  • Add FE contract tests or UI copy only after BE behavior is explicit.
  • For show_score_immediately, decide whether exam-level cbt_show_result overrides policy or policy should be source of truth.

Medium

M1. CSV/XLSX import does not support true multi-correct mcq_multi

Impact:

  • Manual authoring supports multiple correct options.
  • Runner/scoring supports multiple correct options.
  • Import template and backend import only support one correct answer A-E.

Recommendation:

  • Extend import contract to allow values like A,C or A|C.
  • Add backend parser tests for mcq_multi.
  • Update FE sample template and validation copy.

M2. Public CBT routes do not carry FE feature flag metadata

Routes:

  • /cbt/public
  • /cbt/public/runner/:exam_id

Impact:

  • Authenticated CBT routes fail closed via FE feature flag.
  • Public CBT routes rely on backend/module availability rather than route guard metadata.

Recommendation:

  • Add featureFlag: "scola_cbt" to public route meta if public routes should also fail closed client-side.
  • Add route guard test for public CBT route behavior.

M3. Pendaftar admission detail parent route is gated by scola_cbt

Route:

  • /spmb/admission/:id(\\d+)

Impact:

  • Detail pendaftar includes non-CBT tabs such as documents, achievements, and enrollment.
  • With featureFlag: "scola_cbt" on parent route, tenants without CBT may be blocked from non-CBT admission detail.

Recommendation:

  • Move scola_cbt gating to CBT-specific child/entry points only, unless the whole admission detail truly depends on CBT.
  • Add router test for pendaftar admission detail with scola_cbt = false.

M4. Academic grading capability naming is broader than route gate

Observed:

  • academics.cbt_grading.manage exists.
  • Teacher grading workspace route uses academics.cbt_exams.manage.
  • Existing test intentionally enforces this.

Impact:

  • Not a functional bug today, but can confuse future RBAC changes.

Recommendation:

  • Document this as intentional, or split route behavior:
  • teacher-managed exam grading via academics.cbt_exams.manage
  • standalone grader access via academics.cbt_grading.manage

Low

L1. Question set sort is local after paginated fetch

Impact:

  • Sorting in FE may only reorder current page, not full dataset.

Recommendation:

  • If global sorting is desired, expose order in listQuestionSets service and pass it to BE.

L2. Question delete endpoint exists but is not exposed in primary FE

Impact:

  • Service has deleteQuestion, BE endpoint exists, but BE restricts delete to CBT Manager.
  • Main question set detail UX does not expose item delete.

Recommendation:

  • Either keep as manager-only backend function, or add explicit manager-only UI if needed.

L3. Tag listing is more permissive than question authoring

Impact:

  • BE tag/list is available to authenticated users, while create/update question requires stronger question access.

Recommendation:

  • Accept if tags are considered harmless metadata lookup, or align access check with question read.

12. Current Test Coverage Relevant to Sync

Frontend

  • tests/menu/cbtMenuRouteRbacSync.spec.js
  • teacher/student/SPMB CBT route/menu alignment
  • internal SPMB CBT route admission scoping
  • tests/menu/teacherCbtFeatureFlag.spec.js
  • teacher CBT routes enforce scola_cbt
  • learning exam create/detail route fail closed for LMS-only tenants
  • tests/menu/teacherCapabilityCoverage.spec.js
  • generic teacher cannot access proctor route
  • grading workspace stays reachable for teacher-managed exams
  • tests/menu/routerFeatureFlagGuard.spec.js
  • admin CBT route blocked when scola_cbt disabled

Backend

  • scola_cbt/tests/test_question_set_contract_unit.py
  • question set access, owner/manager update/delete, delete guard for active exams
  • scola_cbt/tests/test_exam_contract_unit.py
  • learning CBT requires grade_component_type
  • update derives is_cbt from question_set_id
  • standard learning exam can exist without question set
  • scola_cbt/tests/test_runner_admission_unit.py
  • authenticated admission runner can start without token
  • scola_cbt/tests/test_runner_timeout_unit.py
  • expired sync auto-submits and does not write late answer
  • scola_cbt/tests/test_grading_contract_unit.py
  • grading payload and access contract
  • scola_cbt/tests/test_proctor_structure_unit.py
  • proctor route structure
  • scola_cbt/tests/test_report_structure_unit.py
  • report/export route structure

13. Suggested Next Steps

  1. Decide source of truth for score visibility:
  2. exam field cbt_show_result
  3. policy field show_score_immediately
  4. or policy default with exam override

  5. Implement or hide unenforced policy fields:

  6. grace period
  7. multi-login blocking
  8. negative marking

  9. Extend question import for mcq_multi:

  10. parse A,C or A|C
  11. validate multiple correct answers
  12. update FE sample rows
  13. add BE unit tests

  14. Tighten FE route gating:

  15. add scola_cbt feature flag to public CBT routes if desired
  16. move SPMB admission detail CBT flag to CBT-only child/entry routes

  17. Clarify grading capability policy:

  18. keep current academics.cbt_exams.manage intentionally
  19. or introduce standalone grader route/capability behavior

  20. Add contract tests for policy semantics:

  21. negative marking scoring
  22. grace period timeout behavior
  23. multi-login block behavior
  24. score visibility response behavior