{
  "schema_version": "t2.2-w01-current-tip-matrix-v1",
  "t22_run_id": "T2.2-W01-20260811T1925Z",
  "w01_runtime_run_id": "T2.1-W01-20260811T192727Z",
  "e12_run_id": "T0-E1.2-REBIND-CURRENT-20260811T1925Z",
  "generated_at": "2026-08-11T19:33:50.050820+00:00",
  "identity": {
    "frontend_application_commit_sha": "a333a8f2f9421b2feabbbbb59609a55b4b2290d2",
    "backend_application_commit_sha": "0bcb032f22ea00cfb8bf7c1b009d071e4172b869",
    "frontend_application_tree_hash": "b91dd4abf0aab2065781dfbe0d1ff61f167754077a3a8ee7b4492bc25f1ddab6",
    "backend_application_tree_hash": "96ef304cfcfd76905582bb46a3532754a8792b52454337d9843d6c6912538b63",
    "runtime_application_tree_hash": "96ef304cfcfd76905582bb46a3532754a8792b52454337d9843d6c6912538b63",
    "frontend_build_checksum": "2558e87d8b790602552936f653cc6cced342af9f34da22adbf1a79e3f41ed38e",
    "served_frontend_identity": {
      "version": "a333a8f2f942+msp1ro7e",
      "builtAt": "2026-08-11T19:23:23.462Z",
      "frontend_git_commit_sha": "a333a8f2f9421b2feabbbbb59609a55b4b2290d2",
      "build_run_id": "fe-build-a333a8f2f942-msp1ro7e"
    },
    "database_name": "scola_golden",
    "database_uuid": "2bf5fd27-9390-11f1-9617-28e8d4be73f5",
    "golden_fixture_version": "gt-core-b1.1",
    "golden_fixture_checksum": "40bc6c2762d27692e7947b6e41b1446c890e833c76728ba0fe640916a2deec96",
    "overlay_version": "gt-w01-1.0",
    "fixture_base": "gt-core-b1.1",
    "installed_modules_checksum": "8574e33e50628d6eb942018a017d6dd6c8bab4667b2b9587729b2c69b799a0d2",
    "entitlements_checksum": "9ba52910fb29cae334c51a704d303baac7ec8996a7158b258b0e42059bad9a6c",
    "route_coverage_checksum": "a50915b1cc50dbff18647cbf6d70256c9e76c4834829325b12421010e72c2140",
    "backend_target_base_url": "http://127.0.0.1:8090"
  },
  "lesson_hour_overlap_rule": "LESSON_HOUR_OVERLAP_FORBIDDEN",
  "totals": {
    "total": 64,
    "passed": 64,
    "failed": 0,
    "pass": true
  },
  "matrix": {
    "run_id": "T2.1-W01-20260811T192727Z",
    "overlay_version": "gt-w01-1.0",
    "fixture_base": "gt-core-b1.1",
    "lesson_hour_overlap_rule": "LESSON_HOUR_OVERLAP_FORBIDDEN",
    "pass": true,
    "total": 64,
    "passed": 64,
    "failed": 0,
    "ctx_ids": {
      "year_id": 36,
      "term_id": 54,
      "t2_year_id": 37,
      "course_id": 32,
      "subject_id": 29,
      "t2_course_id": 33,
      "t2_subject_id": 30,
      "batch_id": 35,
      "t2_batch_id": 36,
      "profile_id": 8,
      "lh1_id": 45,
      "lh2_id": 46,
      "session_id": 62,
      "session_id_2": 63,
      "t21_year_id": 38,
      "t21_term_ids": [
        57,
        58
      ],
      "t21_batch_b": 37,
      "t21_sid_a": 66,
      "t21_sid_b": 67
    },
    "seed": {
      "profile_t1": 3,
      "profile_t2": 4,
      "faculty_t1_teacher": 3,
      "faculty_t1_homeroom": 4,
      "faculty_t2_teacher": 5
    },
    "cases": [
      {
        "case_id": "W01-AY-CREATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": "empty or existing years company 20",
        "action": "POST /api/v1/academic/academic-years/save",
        "expected": "persisted year company_id=20 with terms",
        "actual": {
          "resp_success": true,
          "persisted": {
            "name": "W01 AY 2026/2027 92727Z",
            "start_date": "2026-07-01",
            "end_date": "2027-06-30",
            "company_id": 20
          }
        },
        "before_state": {
          "count": 3
        },
        "after_state": {
          "count": 4,
          "id": 36
        },
        "notes": ""
      },
      {
        "case_id": "W01-AY-INVALID-RANGE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create year end<start",
        "expected": "validation failure; no new invalid year",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Tanggal selesai tahun ajaran harus pada atau setelah tanggal mulai."
        },
        "before_state": {
          "count": 4
        },
        "after_state": {
          "count": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-AY-CROSS-TENANT-LIST",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "list academic years",
        "expected": "GT-T2 year id not visible",
        "actual": {
          "leaked": false,
          "count": 4
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-AY-CROSS-TENANT-MUTATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "mutate GT-T2 year by id",
        "expected": "denied; name unchanged",
        "actual": {
          "success": false,
          "error": "access_denied",
          "message": "Tahun ajaran berada di luar scope sekolah."
        },
        "before_state": null,
        "after_state": {
          "name": "W01 AY T2 2026/2027 92727Z"
        },
        "notes": ""
      },
      {
        "case_id": "W01-GRADE-CREATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create grade level",
        "expected": "op.course company 20",
        "actual": {
          "id": 32,
          "error": null,
          "message": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-SUBJECT-CREATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create subject",
        "expected": "persisted subject",
        "actual": {
          "id": 29,
          "message": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-BATCH-CREATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create study group / rombel",
        "expected": "op.batch company 20 with year/course",
        "actual": {
          "id": 35,
          "error": null,
          "message": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-BATCH-FOREIGN-COURSE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create batch with GT-T2 course",
        "expected": "denied / validation fail",
        "actual": {
          "success": false,
          "error": "access_denied",
          "message": "Jenjang/kelas berada di luar scope sekolah."
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-TEACHER-ASSIGN-HOMEROOM",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "assign faculty_id on batch",
        "expected": "batch.faculty_id updated (Core; not LMS teaching.assignment)",
        "actual": {
          "faculty_id": 3
        },
        "before_state": null,
        "after_state": null,
        "notes": "op.teaching.assignment classified OPTIONAL/LMS — absent on Golden Core"
      },
      {
        "case_id": "W01-TEACHER-ASSIGN-FOREIGN",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "assign GT-T2 faculty to GT-T1 batch",
        "expected": "denied; faculty unchanged",
        "actual": {
          "success": false,
          "error": "access_denied",
          "message": "Terdapat guru di luar scope sekolah."
        },
        "before_state": null,
        "after_state": {
          "faculty_id": 3
        },
        "notes": ""
      },
      {
        "case_id": "W01-LH-PROFILE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "ensure-default lesson hour profile",
        "expected": "profile id",
        "actual": {
          "profile_id": 8,
          "raw_keys": [
            "item"
          ]
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-LH-CREATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create lesson hour JP1",
        "expected": "persisted scola.lesson.hours",
        "actual": {
          "id": 45,
          "message": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-LH-INVALID-RANGE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create start>=end lesson hour",
        "expected": "rejected",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Jam selesai harus setelah jam mulai."
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-CREATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "POST sessions/save",
        "expected": "persisted op.session with relations + company",
        "actual": {
          "item": {
            "id": 62,
            "faculty_id": [
              3,
              "GT TeacherA"
            ],
            "subject_id": [
              29,
              "Matematika W01 92727Z"
            ],
            "batch_id": [
              35,
              "7A W01 92727Z"
            ],
            "course_id": [
              32,
              "Kelas 7 92727Z"
            ],
            "classroom_id": false,
            "start_datetime": "2026-08-12 12:30:00",
            "end_datetime": "2026-08-12 13:15:00",
            "lesson_hours_id": [
              45,
              "JP1-92727Z (19:30 - 20:15)"
            ]
          },
          "persisted": {
            "batch_id": 35,
            "subject_id": 29,
            "faculty_id": 3,
            "lesson_hours_id": 45,
            "company_id": 20,
            "start_datetime": "2026-08-12 12:30:00",
            "end_datetime": "2026-08-12 13:15:00"
          },
          "message": null
        },
        "before_state": {
          "count": 0
        },
        "after_state": {
          "count": 1,
          "id": 62
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-TEACHER-CLASS-CONFLICT",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "duplicate overlapping session",
        "expected": "conflict; no extra row",
        "actual": {
          "success": false,
          "error": "conflict",
          "message": "Rombel 7A W01 92727Z sudah memiliki sesi lain pada jam yang sama.\nGuru GT TeacherA sudah mengajar pada jam yang sama."
        },
        "before_state": {
          "count": 1
        },
        "after_state": {
          "count": 1
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-UPDATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "move session datetime",
        "expected": "persisted new times; ownership kept",
        "actual": {
          "before": {
            "start_datetime": "2026-08-12 12:30:00",
            "end_datetime": "2026-08-12 13:15:00",
            "faculty_id": 3
          },
          "after": {
            "start_datetime": "2026-08-13 12:30:00",
            "end_datetime": "2026-08-13 13:15:00",
            "faculty_id": 3
          }
        },
        "before_state": {
          "start_datetime": "2026-08-12 12:30:00",
          "end_datetime": "2026-08-12 13:15:00",
          "faculty_id": 3
        },
        "after_state": {
          "start_datetime": "2026-08-13 12:30:00",
          "end_datetime": "2026-08-13 13:15:00",
          "faculty_id": 3
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-UPDATE-ATOMIC",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "update into conflict with other session",
        "expected": "denied; original unchanged",
        "actual": {
          "success": false,
          "error": "conflict",
          "message": "Sesi bentrok dengan jadwal yang sama (faculty/waktu)."
        },
        "before_state": {
          "start_datetime": "2026-08-13 12:30:00",
          "end_datetime": "2026-08-13 13:15:00",
          "faculty_id": 3
        },
        "after_state": {
          "start_datetime": "2026-08-13 12:30:00",
          "end_datetime": "2026-08-13 13:15:00",
          "faculty_id": 3
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-FOREIGN-BATCH",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create session with foreign relation",
        "expected": "denied without existence leak; no row",
        "actual": {
          "error": "access_denied",
          "message": "Referensi akademik berada di luar scope sekolah."
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-FOREIGN-SUBJECT",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create session with foreign relation",
        "expected": "denied without existence leak; no row",
        "actual": {
          "error": "access_denied",
          "message": "Referensi akademik berada di luar scope sekolah."
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-FOREIGN-FACULTY",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create session with foreign relation",
        "expected": "denied without existence leak; no row",
        "actual": {
          "error": "access_denied",
          "message": "Referensi akademik berada di luar scope sekolah."
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-TEACHER-MUTATE-DENIED",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "sessions/save",
        "expected": "denied; no mutation",
        "actual": {
          "success": false,
          "error": "capability_denied",
          "message": "Insufficient permissions for this API route.",
          "capability": "academics.schedule.manage",
          "path": "/api/v1/academic/sessions/save"
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-STUDENT-MUTATE-DENIED",
        "status": "PASS",
        "actor": "gt.student.s1",
        "precondition": null,
        "action": "sessions/save",
        "expected": "denied; no mutation",
        "actual": {
          "success": false,
          "error": "access_denied",
          "message": "Anda tidak memiliki izin mengubah jadwal pelajaran."
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-PARENT-MUTATE-DENIED",
        "status": "PASS",
        "actor": "gt.parent.p1",
        "precondition": null,
        "action": "sessions/save",
        "expected": "denied; no mutation",
        "actual": {
          "success": false,
          "error": "capability_denied",
          "message": "Insufficient permissions for this API route.",
          "capability": "academics.schedule.manage",
          "path": "/api/v1/academic/sessions/save"
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-RPC-BYPASS",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session create",
        "expected": "no unauthorized create",
        "actual": {
          "http": 200,
          "error": true,
          "result": null
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-READ-CONSISTENCY",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "list sessions vs DB",
        "expected": "semantic agreement",
        "actual": {
          "found": {
            "id": 62,
            "faculty_id": [
              3,
              "GT TeacherA"
            ],
            "subject_id": [
              29,
              "Matematika W01 92727Z"
            ],
            "batch_id": [
              35,
              "7A W01 92727Z"
            ],
            "course_id": [
              32,
              "Kelas 7 92727Z"
            ],
            "classroom_id": false,
            "start_datetime": "2026-08-13 12:30:00",
            "end_datetime": "2026-08-13 13:15:00",
            "lesson_hours_id": [
              45,
              "JP1-92727Z (19:30 - 20:15)"
            ]
          },
          "db": {
            "batch_id": 35,
            "subject_id": 29,
            "faculty_id": 3
          }
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-DELETE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "sessions/delete",
        "expected": "row removed",
        "actual": {
          "success": true,
          "data": {
            "deleted": 1
          }
        },
        "before_state": {
          "exists": 1
        },
        "after_state": {
          "exists": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-CONCURRENT",
        "status": "PASS",
        "actor": "gt.school_admin x2",
        "precondition": null,
        "action": "two concurrent identical session creates",
        "expected": "exactly one persisted",
        "actual": {
          "successes": 1,
          "created_ids": [
            64
          ],
          "messages": [
            null,
            "Sesi bentrok dengan jadwal yang sama (faculty/waktu)."
          ]
        },
        "before_state": {
          "count": 1
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-SESSION-IDEMPOTENT-DUP",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "sequential duplicate submit",
        "expected": "rejected; no second row",
        "actual": {
          "success": false,
          "error": "conflict",
          "message": "Rombel 7A W01 92727Z sudah memiliki sesi lain pada jam yang sama.\nGuru GT TeacherA sudah mengajar pada jam yang sama."
        },
        "before_state": {
          "count": 2
        },
        "after_state": {
          "count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-TERM-CREATE-VALID",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create year + 2 valid terms",
        "expected": "year + both terms persisted",
        "actual": {
          "year_id": 38,
          "term_count": 2
        },
        "before_state": {
          "year_count": 0
        },
        "after_state": {
          "year_count": 1,
          "term_count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-TERM-START-AFTER-END",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create year with invalid nested term(s)",
        "expected": "validation_error; zero new year; zero new terms",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Tanggal selesai semester harus pada atau setelah tanggal mulai."
        },
        "before_state": {
          "year": 0,
          "terms": 0
        },
        "after_state": {
          "year": 0,
          "terms": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-TERM-BEFORE-YEAR",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create year with invalid nested term(s)",
        "expected": "validation_error; zero new year; zero new terms",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Rentang semester harus berada di dalam tahun ajaran."
        },
        "before_state": {
          "year": 0,
          "terms": 0
        },
        "after_state": {
          "year": 0,
          "terms": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-TERM-AFTER-YEAR",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create year with invalid nested term(s)",
        "expected": "validation_error; zero new year; zero new terms",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Rentang semester harus berada di dalam tahun ajaran."
        },
        "before_state": {
          "year": 0,
          "terms": 0
        },
        "after_state": {
          "year": 0,
          "terms": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-TERM-MIXED",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create year with invalid nested term(s)",
        "expected": "validation_error; zero new year; zero new terms",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Rentang semester harus berada di dalam tahun ajaran."
        },
        "before_state": {
          "year": 0,
          "terms": 0
        },
        "after_state": {
          "year": 0,
          "terms": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-TERM-UPDATE-ATOMIC",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "update year+invalid term with replace_terms",
        "expected": "denied; year and terms unchanged",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Rentang semester harus berada di dalam tahun ajaran."
        },
        "before_state": {
          "year": {
            "name": "T21 AY VALID 92727Z",
            "start_date": "2028-07-01",
            "end_date": "2029-06-30"
          },
          "terms": [
            {
              "name": "S1",
              "term_start_date": "2028-07-01",
              "term_end_date": "2028-12-20"
            },
            {
              "name": "S2",
              "term_start_date": "2029-01-08",
              "term_end_date": "2029-06-30"
            }
          ],
          "term_count": 2
        },
        "after_state": {
          "year": {
            "name": "T21 AY VALID 92727Z",
            "start_date": "2028-07-01",
            "end_date": "2029-06-30"
          },
          "terms": [
            {
              "name": "S1",
              "term_start_date": "2028-07-01",
              "term_end_date": "2028-12-20"
            },
            {
              "name": "S2",
              "term_start_date": "2029-01-08",
              "term_end_date": "2029-06-30"
            }
          ],
          "term_count": 2
        },
        "notes": ""
      },
      {
        "case_id": "W01-TERM-CROSS-TENANT",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "mutate GT-T2 year+terms by id",
        "expected": "denied; foreign year/terms unchanged",
        "actual": {
          "success": false,
          "error": "access_denied",
          "message": "Tahun ajaran berada di luar scope sekolah."
        },
        "before_state": {
          "year": {
            "name": "W01 AY T2 2026/2027 92727Z",
            "company_id": 22
          },
          "term_count": 1
        },
        "after_state": {
          "year": {
            "name": "W01 AY T2 2026/2027 92727Z",
            "company_id": 22
          },
          "term_count": 1
        },
        "notes": ""
      },
      {
        "case_id": "W01-LH-OVERLAP-FORBIDDEN",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create overlapping lesson hour without force_overlap",
        "expected": "rejected; no partial row",
        "actual": {
          "rule": "LESSON_HOUR_OVERLAP_FORBIDDEN",
          "result": {
            "success": false,
            "error": "validation_error",
            "message": "Jam pelajaran bentrok dengan:\n• JP1-92727Z (19:30 - 20:15)\n\nJika Anda yakin ingin melanjutkan, silakan tutup pesan ini dan centang opsi \"Izinkan Bentrok\" di bagian Opsi Lanjutan, lalu simpan kembali."
          }
        },
        "before_state": {
          "count": 0
        },
        "after_state": {
          "count": 0
        },
        "notes": "Source constraint: scola.lesson.hours._check_hours"
      },
      {
        "case_id": "W01-LH-INVALID-NO-PARTIAL",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create start>=end lesson hour",
        "expected": "rejected; no row",
        "actual": {
          "success": false,
          "error": "validation_error",
          "message": "Jam selesai harus setelah jam mulai."
        },
        "before_state": {
          "count": 0
        },
        "after_state": {
          "count": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-LH-FOREIGN-PROFILE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create lesson hour with GT-T2 profile_id",
        "expected": "denied; no row",
        "actual": {
          "result": {
            "success": false,
            "error": "access_denied",
            "message": "Profil jam pelajaran berada di luar scope sekolah."
          }
        },
        "before_state": {
          "count": 0
        },
        "after_state": {
          "count": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-READ-SCOPE-SETUP",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "create same-tenant session A/B for teacher scope",
        "expected": "distinct faculty/batch sessions persisted",
        "actual": {
          "sid_a": 66,
          "sid_b": 67,
          "db_a": {
            "faculty_id": 3,
            "batch_id": 35
          },
          "db_b": {
            "faculty_id": 4,
            "batch_id": 37
          }
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-READ-OWN",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "sessions/list",
        "expected": "session A (66) visible",
        "actual": {
          "ids_sample": [
            62,
            64,
            66
          ],
          "has_a": true
        },
        "before_state": null,
        "after_state": {
          "db_a": {
            "faculty_id": 3,
            "batch_id": 35
          }
        },
        "notes": ""
      },
      {
        "case_id": "W01-READ-OTHER",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "sessions/list",
        "expected": "session B (67) hidden",
        "actual": {
          "has_b": false,
          "total": 3
        },
        "before_state": null,
        "after_state": {
          "db_b": {
            "faculty_id": 4,
            "batch_id": 37
          }
        },
        "notes": ""
      },
      {
        "case_id": "W01-READ-OTHER-DIRECT-ID",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "sessions/get other id",
        "expected": "denied/hidden without detail leak",
        "actual": {
          "success": false,
          "error": "access_denied",
          "message": "Sesi tidak ditemukan."
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-READ-OTHER-FILTER",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "sessions/list with foreign faculty/batch filters",
        "expected": "filter does not widen; B still hidden",
        "actual": {
          "ids": [],
          "has_b": false
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-READ-COUNT",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "sessions/count",
        "expected": "count matches scoped list; excludes B",
        "actual": {
          "count": 3,
          "list_total": 3
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-READ-HOMEROOM-OWN-BATCH",
        "status": "PASS",
        "actor": "gt.homeroom.7a",
        "precondition": null,
        "action": "sessions/list as homeroom",
        "expected": "homeroom batch session B visible",
        "actual": {
          "has_b": true,
          "has_a": false
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-READ-HOMEROOM-UNRELATED-HIDDEN",
        "status": "PASS",
        "actor": "gt.homeroom.7a",
        "precondition": null,
        "action": "sessions/list as homeroom",
        "expected": "unrelated faculty/batch session A hidden",
        "actual": {
          "has_a": false
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-RPC-TEACHER-CREATE",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session.create",
        "expected": "no row created (product API policy)",
        "actual": {
          "http": 200,
          "error": {
            "code": 200,
            "message": "Odoo Server Error",
            "data": {
              "name": "odoo.exceptions.AccessError",
              "debug": "Traceback (most recent call last):\n  File \"/home/scola/odoo/odoo/http.py\", line 1764, in _serve_db\n    return service_model.retrying(self._serve_ir_http, self.env)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/service/model.py\", line 133, in retrying\n    result = func()\n             ^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1791, in _serve_ir_http\n    response = self.dispatcher.dispatch(rule.endpoint, args)\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1995, in dispatch\n    result = self.request.registry['ir.http']._dispatch(endpoint)\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 527, in _dispatch\n    return cls._build_capability_gate_response(\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 242, in _build_capability_gate_response\n    raise AccessError(\nodoo.exceptions.AccessError: /web/dataset mutations are out of scope for Scola product actors; use registered /api routes.\n",
              "message": "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes.",
              "arguments": [
                "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes."
              ],
              "context": {}
            }
          },
          "result": null
        },
        "before_state": {
          "count": 4
        },
        "after_state": {
          "count": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-TEACHER-WRITE",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session.write",
        "expected": "no mutation",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "after_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-TEACHER-UNLINK",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session.unlink",
        "expected": "row retained",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "exists": 1
        },
        "after_state": {
          "exists": 1
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-STUDENT-CREATE",
        "status": "PASS",
        "actor": "gt.student.s1",
        "precondition": null,
        "action": "call_kw op.session.create",
        "expected": "no row created (product API policy)",
        "actual": {
          "http": 200,
          "error": {
            "code": 200,
            "message": "Odoo Server Error",
            "data": {
              "name": "odoo.exceptions.AccessError",
              "debug": "Traceback (most recent call last):\n  File \"/home/scola/odoo/odoo/http.py\", line 1764, in _serve_db\n    return service_model.retrying(self._serve_ir_http, self.env)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/service/model.py\", line 133, in retrying\n    result = func()\n             ^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1791, in _serve_ir_http\n    response = self.dispatcher.dispatch(rule.endpoint, args)\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1995, in dispatch\n    result = self.request.registry['ir.http']._dispatch(endpoint)\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 527, in _dispatch\n    return cls._build_capability_gate_response(\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 242, in _build_capability_gate_response\n    raise AccessError(\nodoo.exceptions.AccessError: /web/dataset mutations are out of scope for Scola product actors; use registered /api routes.\n",
              "message": "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes.",
              "arguments": [
                "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes."
              ],
              "context": {}
            }
          },
          "result": null
        },
        "before_state": {
          "count": 4
        },
        "after_state": {
          "count": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-STUDENT-WRITE",
        "status": "PASS",
        "actor": "gt.student.s1",
        "precondition": null,
        "action": "call_kw op.session.write",
        "expected": "no mutation",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "after_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-STUDENT-UNLINK",
        "status": "PASS",
        "actor": "gt.student.s1",
        "precondition": null,
        "action": "call_kw op.session.unlink",
        "expected": "row retained",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "exists": 1
        },
        "after_state": {
          "exists": 1
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-PARENT-CREATE",
        "status": "PASS",
        "actor": "gt.parent.p1",
        "precondition": null,
        "action": "call_kw op.session.create",
        "expected": "no row created (product API policy)",
        "actual": {
          "http": 200,
          "error": {
            "code": 200,
            "message": "Odoo Server Error",
            "data": {
              "name": "odoo.exceptions.AccessError",
              "debug": "Traceback (most recent call last):\n  File \"/home/scola/odoo/odoo/http.py\", line 1764, in _serve_db\n    return service_model.retrying(self._serve_ir_http, self.env)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/service/model.py\", line 133, in retrying\n    result = func()\n             ^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1791, in _serve_ir_http\n    response = self.dispatcher.dispatch(rule.endpoint, args)\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1995, in dispatch\n    result = self.request.registry['ir.http']._dispatch(endpoint)\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 527, in _dispatch\n    return cls._build_capability_gate_response(\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 242, in _build_capability_gate_response\n    raise AccessError(\nodoo.exceptions.AccessError: /web/dataset mutations are out of scope for Scola product actors; use registered /api routes.\n",
              "message": "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes.",
              "arguments": [
                "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes."
              ],
              "context": {}
            }
          },
          "result": null
        },
        "before_state": {
          "count": 4
        },
        "after_state": {
          "count": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-PARENT-WRITE",
        "status": "PASS",
        "actor": "gt.parent.p1",
        "precondition": null,
        "action": "call_kw op.session.write",
        "expected": "no mutation",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "after_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-PARENT-UNLINK",
        "status": "PASS",
        "actor": "gt.parent.p1",
        "precondition": null,
        "action": "call_kw op.session.unlink",
        "expected": "row retained",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "exists": 1
        },
        "after_state": {
          "exists": 1
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-ADMIN-CREATE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "call_kw op.session.create",
        "expected": "no row created (product API policy)",
        "actual": {
          "http": 200,
          "error": {
            "code": 200,
            "message": "Odoo Server Error",
            "data": {
              "name": "odoo.exceptions.AccessError",
              "debug": "Traceback (most recent call last):\n  File \"/home/scola/odoo/odoo/http.py\", line 1764, in _serve_db\n    return service_model.retrying(self._serve_ir_http, self.env)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/service/model.py\", line 133, in retrying\n    result = func()\n             ^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1791, in _serve_ir_http\n    response = self.dispatcher.dispatch(rule.endpoint, args)\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/odoo/http.py\", line 1995, in dispatch\n    result = self.request.registry['ir.http']._dispatch(endpoint)\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 527, in _dispatch\n    return cls._build_capability_gate_response(\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/home/scola/odoo/custom_addons_scola/gcgscola/scola_core/models/ir_http.py\", line 242, in _build_capability_gate_response\n    raise AccessError(\nodoo.exceptions.AccessError: /web/dataset mutations are out of scope for Scola product actors; use registered /api routes.\n",
              "message": "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes.",
              "arguments": [
                "/web/dataset mutations are out of scope for Scola product actors; use registered /api routes."
              ],
              "context": {}
            }
          },
          "result": null
        },
        "before_state": {
          "count": 4
        },
        "after_state": {
          "count": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-ADMIN-WRITE",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "call_kw op.session.write",
        "expected": "no mutation",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "after_state": {
          "start_datetime": "2026-09-01 13:30:00",
          "faculty_id": 4
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-ADMIN-UNLINK",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "call_kw op.session.unlink",
        "expected": "row retained",
        "actual": {
          "http": 200,
          "error": true
        },
        "before_state": {
          "exists": 1
        },
        "after_state": {
          "exists": 1
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-TEACHER-READ-SEARCH",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session.search for session B",
        "expected": "denied or empty; B not disclosed",
        "actual": {
          "http": 200,
          "error": true,
          "result": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-RPC-TEACHER-READ-SEARCH_READ",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session.search_read for session B",
        "expected": "denied or empty; B not disclosed",
        "actual": {
          "http": 200,
          "error": true,
          "result": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-RPC-TEACHER-READ-READ",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session.read for session B",
        "expected": "denied or empty; B not disclosed",
        "actual": {
          "http": 200,
          "error": true,
          "result": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-RPC-TEACHER-READ-SEARCH_COUNT",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.session.search_count for session B",
        "expected": "denied or empty; B not disclosed",
        "actual": {
          "http": 200,
          "error": true,
          "result": null
        },
        "before_state": null,
        "after_state": null,
        "notes": ""
      },
      {
        "case_id": "W01-RPC-AY-CREATE-TEACHER",
        "status": "PASS",
        "actor": "gt.teacher.assigned",
        "precondition": null,
        "action": "call_kw op.academic.year.create",
        "expected": "no year created",
        "actual": {
          "http": 200,
          "error": true,
          "result": null
        },
        "before_state": {
          "count": 0
        },
        "after_state": {
          "count": 0
        },
        "notes": ""
      },
      {
        "case_id": "W01-RPC-AY-CREATE-SCHOOL_ADMIN",
        "status": "PASS",
        "actor": "gt.school_admin",
        "precondition": null,
        "action": "call_kw op.academic.year.create",
        "expected": "no year created",
        "actual": {
          "http": 200,
          "error": true,
          "result": null
        },
        "before_state": {
          "count": 0
        },
        "after_state": {
          "count": 0
        },
        "notes": ""
      }
    ]
  }
}
