{
  "run_id": "T3-W02-20260829T010533Z",
  "overlay_version": "gt-w02-1.0",
  "fixture_base": "gt-core-b1.1",
  "certified_claim": false,
  "pass": true,
  "total": 33,
  "passed": 33,
  "failed": 0,
  "skipped": 0,
  "backend_sha": "6033c211fcb079e962d6e48960a6c7f437b216be",
  "frontend_sha": "40467af7aa7f6cd7be7bb5e8936132ba846ba184",
  "ctx_ids": {
    "batch_t1": 161,
    "batch_t2": 160,
    "create_ok_student_id": 290,
    "create_ok_nsn": "9020000001",
    "sibling_parent_id": 261,
    "t2_student_id": 295,
    "rpc_target_id": 301,
    "scope_sid_a": 302,
    "scope_sid_b": 303
  },
  "seed": {
    "batch_t1": 161,
    "batch_t2": 160,
    "course_t1": 115,
    "course_t2": 116,
    "year_t1": 30,
    "year_t2": 84,
    "overlay": "gt-w02-1.0"
  },
  "cleanup": {
    "students": 15,
    "parents": 14,
    "partners": 32,
    "users_deactivated": 0,
    "batches": 0,
    "years": 0,
    "courses": 0
  },
  "cases": [
    {
      "case_id": "W02-CREATE-OK",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": "batch_t1 exists; create_accounts=false",
      "action": "POST /api/v1/admin/import/students-with-parents 1 student+parent with batch_id",
      "expected": "op.student company 20 + enrollment + parent link; no new user",
      "actual": {
        "success": true,
        "success_count": 1,
        "errors": [],
        "snap": {
          "exists": true,
          "id": 290,
          "name": "W02-CreateOK 10533Z",
          "nsn": "9020000001",
          "company_id": 20,
          "current_batch_id": 161,
          "enroll_batches": [
            161
          ],
          "parent_ids": [
            258
          ],
          "user_id": false
        },
        "users_delta": 0
      },
      "before_state": {
        "students_with_nsn": 0
      },
      "after_state": {
        "students_with_nsn": 1,
        "student_id": 290
      },
      "notes": ""
    },
    {
      "case_id": "W02-CREATE-FOREIGN-BATCH",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "import with GT-T2 batch_id",
      "expected": "fail; no student created",
      "actual": {
        "result": {
          "success": false,
          "error": "PRE_VALIDATION_FAILED",
          "message": "Ditemukan 1 kesalahan validasi. Impor dibatalkan.",
          "error_count": null,
          "errors": [
            {
              "row": 1,
              "field": "batch_id",
              "code": "FOREIGN_OR_INVALID_BATCH",
              "message": "Rombel/kelas tidak valid untuk unit sekolah ini."
            }
          ],
          "success_count": null
        }
      },
      "before_state": {
        "count": 0
      },
      "after_state": {
        "count": 0
      },
      "notes": ""
    },
    {
      "case_id": "W02-CREATE-DUP-NSN",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "second create same nsn",
      "expected": "fail; still one student",
      "actual": {
        "error": "PRE_VALIDATION_FAILED",
        "errors": [
          {
            "row": 1,
            "field": "nsn",
            "code": "DUPLICATE_IN_DB",
            "message": "NISN 9020000001 sudah terdaftar di sistem untuk W02-CreateOK 10533Z."
          }
        ],
        "success_count": null
      },
      "before_state": {
        "count": 1
      },
      "after_state": {
        "count": 1
      },
      "notes": ""
    },
    {
      "case_id": "W02-IMPORT-MIXED-ROW",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "import valid+invalid+valid strict_mode=false",
      "expected": "valid persist; invalid no partial (ROW_ATOMIC)",
      "actual": {
        "success_count": 2,
        "error_count": 1,
        "errors": [
          {
            "row": 2,
            "field": "nsn",
            "code": "INVALID_FORMAT",
            "message": "NISN harus tepat 10 digit."
          }
        ]
      },
      "before_state": {
        "a": 0,
        "c": 0,
        "bad": 0
      },
      "after_state": {
        "a": 1,
        "c": 1,
        "bad": 0
      },
      "notes": ""
    },
    {
      "case_id": "W02-SIBLING-PARENT-REUSE",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "import two students same parent_name+mobile",
      "expected": "one op.parent (or one partner) linked to both",
      "actual": {
        "import": {
          "success_count": 2,
          "errors": []
        },
        "db": {
          "partners": 1,
          "parents": 1,
          "parent_ids": [
            261
          ],
          "s1": 293,
          "s2": 294,
          "linked": [
            293,
            294
          ],
          "s1_parents": [
            261
          ],
          "s2_parents": [
            261
          ]
        }
      },
      "before_state": {
        "partners": 0,
        "parents": 0,
        "parent_ids": []
      },
      "after_state": {
        "partners": 1,
        "parents": 1,
        "parent_ids": [
          261
        ],
        "s1": 293,
        "s2": 294,
        "linked": [
          293,
          294
        ],
        "s1_parents": [
          261
        ],
        "s2_parents": [
          261
        ]
      },
      "notes": ""
    },
    {
      "case_id": "W02-ADMIN-FOREIGN-DETAIL",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "T1 admin detail T2 student id",
      "expected": "not_found/denied; T2 row still exists",
      "actual": {
        "detail": {
          "success": false,
          "error": "not_found",
          "message": "Data siswa tidak ditemukan."
        },
        "t2_student_id": 295,
        "import_ok": true
      },
      "before_state": {
        "t2_created": true
      },
      "after_state": {
        "exists": true
      },
      "notes": ""
    },
    {
      "case_id": "W02-ADMIN-LIST-OWN",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "students/list",
      "expected": "T2 student id not visible; T1 list succeeds",
      "actual": {
        "total": 8,
        "leaked_t2": false,
        "owns_create_ok": true
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-TEACHER-IMPORT-DENIED",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "teacher import students-with-parents",
      "expected": "denied; no student",
      "actual": {
        "http": 200,
        "result": {
          "success": false,
          "error": "capability_denied",
          "message": "Insufficient permissions for this API route.",
          "capability": "students.manage",
          "path": "/api/v1/admin/import/students-with-parents"
        }
      },
      "before_state": {
        "count": 0
      },
      "after_state": {
        "count": 0
      },
      "notes": ""
    },
    {
      "case_id": "W02-UPDATE-ATOMIC",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "students/save duplicate nsn update",
      "expected": "update fails; original nsn/name intact",
      "actual": {
        "save": {
          "success": false,
          "error": "odoo_server_error",
          "message": "The operation cannot be completed: NISN must be unique per school unit!",
          "exception": "odoo.exceptions.ValidationError"
        },
        "before_nsn": "9020000009",
        "after_nsn": "9020000009"
      },
      "before_state": {
        "exists": true,
        "id": 296,
        "name": "W02-UpdA 10533Z",
        "nsn": "9020000009",
        "company_id": 20,
        "current_batch_id": 161,
        "enroll_batches": [
          161
        ],
        "parent_ids": [
          263
        ],
        "user_id": false
      },
      "after_state": {
        "exists": true,
        "id": 296,
        "name": "W02-UpdA 10533Z",
        "nsn": "9020000009",
        "company_id": 20,
        "current_batch_id": 161,
        "enroll_batches": [
          161
        ],
        "parent_ids": [
          263
        ],
        "user_id": false
      },
      "notes": ""
    },
    {
      "case_id": "W02-UPDATE-PARENT-BOUNDARY",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": "Product: students/save and parents/relation/save are separate txs",
      "action": "save student then fail relation/save with foreign student",
      "expected": "student profile intact; foreign link rejected",
      "actual": {
        "save": true,
        "relation": {
          "success": false,
          "error": "scope_error",
          "message": "Ada siswa di luar scope sekolah Anda."
        },
        "nickname": "W02-BoundA 10533Z"
      },
      "before_state": {
        "exists": true,
        "id": 298,
        "name": "W02-BoundA 10533Z",
        "nsn": "9020000011",
        "company_id": 20,
        "current_batch_id": 161,
        "enroll_batches": [
          161
        ],
        "parent_ids": [
          265
        ],
        "user_id": false
      },
      "after_state": {
        "exists": true,
        "id": 298,
        "name": "W02-BoundA 10533Z",
        "nsn": "9020000011",
        "company_id": 20,
        "current_batch_id": 161,
        "enroll_batches": [
          161
        ],
        "parent_ids": [
          265
        ],
        "user_id": false
      },
      "notes": "G-004 Product boundary documented + proven"
    },
    {
      "case_id": "W02-PARENT-FOREIGN-PARTNER-NO-EXISTING",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": "T2 partner company 22 with zero op.parent rows",
      "action": "T1 admin parents/relation/save partner_id + T1 student",
      "expected": "scope_error; no Parent.create; student parent_ids unchanged",
      "actual": {
        "setup": {
          "partner_id": 997,
          "existing_parent": 0
        },
        "relation": {
          "success": false,
          "error": "scope_error",
          "message": "Orang tua di luar scope sekolah Anda."
        },
        "before_parents": 0,
        "after_parents": 0,
        "before_links": [
          266
        ],
        "after_links": [
          266
        ]
      },
      "before_state": null,
      "after_state": null,
      "notes": "T3W02-F-001/F-003 adversarial create branch"
    },
    {
      "case_id": "W02-DOUBLE-SUBMIT",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "same import payload twice",
      "expected": "exactly one student; second rejected as dup",
      "actual": {
        "first": 1,
        "second_error": "PRE_VALIDATION_FAILED",
        "second_errors": [
          {
            "row": 1,
            "field": "nsn",
            "code": "DUPLICATE_IN_DB",
            "message": "NISN 9020000013 sudah terdaftar di sistem untuk W02-Double 10533Z."
          }
        ],
        "count": 1
      },
      "before_state": {
        "count": 0
      },
      "after_state": {
        "count": 1
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-TEACHER-CREATE",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "call_kw op.student.create",
      "expected": "denied / no row (product API policy)",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": {
        "count": 0
      },
      "after_state": {
        "count": 0
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-STUDENT-CREATE",
      "status": "PASS",
      "actor": "gt.student.s1",
      "precondition": null,
      "action": "call_kw op.student.create",
      "expected": "denied / no row (product API policy)",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": {
        "count": 0
      },
      "after_state": {
        "count": 0
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-PARENT-CREATE",
      "status": "PASS",
      "actor": "gt.parent.p1",
      "precondition": null,
      "action": "call_kw op.student.create",
      "expected": "denied / no row (product API policy)",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": {
        "count": 0
      },
      "after_state": {
        "count": 0
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-ADMIN-CREATE",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "call_kw op.student.create",
      "expected": "denied / no row (product API policy)",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": {
        "count": 0
      },
      "after_state": {
        "count": 0
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-TEACHER-WRITE",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "call_kw op.student.write",
      "expected": "no mutation",
      "actual": {
        "http": 200,
        "error": true
      },
      "before_state": {
        "name": "W02-RPC-Target 10533Z",
        "nsn": "9020000015"
      },
      "after_state": {
        "name": "W02-RPC-Target 10533Z",
        "nsn": "9020000015"
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-TEACHER-UNLINK",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "call_kw op.student.unlink",
      "expected": "row retained",
      "actual": {
        "http": 200,
        "error": true
      },
      "before_state": {
        "exists": 1
      },
      "after_state": {
        "exists": 1
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-STUDENT-WRITE",
      "status": "PASS",
      "actor": "gt.student.s1",
      "precondition": null,
      "action": "call_kw op.student.write",
      "expected": "no mutation",
      "actual": {
        "http": 200,
        "error": true
      },
      "before_state": {
        "name": "W02-RPC-Target 10533Z",
        "nsn": "9020000015"
      },
      "after_state": {
        "name": "W02-RPC-Target 10533Z",
        "nsn": "9020000015"
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-STUDENT-UNLINK",
      "status": "PASS",
      "actor": "gt.student.s1",
      "precondition": null,
      "action": "call_kw op.student.unlink",
      "expected": "row retained",
      "actual": {
        "http": 200,
        "error": true
      },
      "before_state": {
        "exists": 1
      },
      "after_state": {
        "exists": 1
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-PARENT-WRITE",
      "status": "PASS",
      "actor": "gt.parent.p1",
      "precondition": null,
      "action": "call_kw op.student.write",
      "expected": "no mutation",
      "actual": {
        "http": 200,
        "error": true
      },
      "before_state": {
        "name": "W02-RPC-Target 10533Z",
        "nsn": "9020000015"
      },
      "after_state": {
        "name": "W02-RPC-Target 10533Z",
        "nsn": "9020000015"
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-PARENT-UNLINK",
      "status": "PASS",
      "actor": "gt.parent.p1",
      "precondition": null,
      "action": "call_kw op.student.unlink",
      "expected": "row retained",
      "actual": {
        "http": 200,
        "error": true
      },
      "before_state": {
        "exists": 1
      },
      "after_state": {
        "exists": 1
      },
      "notes": ""
    },
    {
      "case_id": "W02-RPC-TEACHER-SEARCH-T2",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "call_kw op.student.search for T2 student",
      "expected": "no T2 exposure",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-RPC-TEACHER-SEARCH-READ-T2",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "call_kw op.student.search_read for T2 student",
      "expected": "no T2 exposure",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-RPC-TEACHER-READ-T2",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "call_kw op.student.read for T2 student",
      "expected": "no T2 exposure",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-RPC-TEACHER-SEARCH-COUNT-T2",
      "status": "PASS",
      "actor": "gt.teacher.assigned",
      "precondition": null,
      "action": "call_kw op.student.search_count for T2 student",
      "expected": "no T2 exposure",
      "actual": {
        "http": 200,
        "error": true,
        "result": null
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-PARENT-SCOPE",
      "status": "PASS",
      "actor": "gt.parent.p1",
      "precondition": null,
      "action": "parent A must not see student B (admin detail + portal if available)",
      "expected": "child A only; B denied/absent",
      "actual": {
        "admin_scope_ok": true,
        "relation_students": [
          302
        ],
        "portal": {
          "bind": {
            "parent_id": 240,
            "kids_db": [
              252,
              302
            ]
          },
          "children_endpoint": "/api/v1/attendance/parent/children",
          "children_success": true,
          "child_ids": [
            252,
            302
          ],
          "foreign_daily": {
            "error": "student_not_found",
            "message": "Siswa tidak ditemukan untuk akun orang tua ini."
          },
          "lms_note": "scola_lms not installed on Golden; LMS /api/parent/children skipped"
        },
        "sid_a": 302,
        "sid_b": 303
      },
      "before_state": {
        "sid_a": 302,
        "sid_b": 303
      },
      "after_state": {
        "admin_scope_ok": true,
        "portal_ok": true
      },
      "notes": "admin detail + portal children"
    },
    {
      "case_id": "W02-PARENT-SCOPE-DASHBOARD",
      "status": "PASS",
      "actor": "gt.parent.p1",
      "precondition": null,
      "action": "POST /api/v1/parent/dashboard",
      "expected": "children contains A only",
      "actual": {
        "success": true,
        "ids": [
          252,
          302
        ],
        "error": null
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-PARENT-SCOPE-DIRECT",
      "status": "PASS",
      "actor": "gt.parent.p1",
      "precondition": null,
      "action": "/api/v1/parent/child/* with A vs B ids",
      "expected": "A ok (possibly empty data); B denied",
      "actual": {
        "own_schedule": true,
        "own_grades": true,
        "foreign_schedule": {
          "success": false,
          "error": "Child not found",
          "message": "Specified child not found in parent's children",
          "timestamp": "2026-08-29T01:07:28+00:00"
        },
        "foreign_grades": {
          "success": false,
          "error": "Child not found",
          "message": "Specified child not found in parent's children",
          "timestamp": "2026-08-29T01:07:28+00:00"
        },
        "foreign_attendance": {
          "success": false,
          "error": "Child not found",
          "message": "Specified child not found in parent's children",
          "timestamp": "2026-08-29T01:07:28+00:00"
        }
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-PARENT-NIK-GLOBAL-UNIQUE",
      "status": "PASS",
      "actor": "shell+gt.school_admin",
      "precondition": null,
      "action": "duplicate NIK same-tenant and cross-tenant",
      "expected": "both rejected; single row; no foreign name leak in error",
      "actual": {
        "parent1": 271,
        "ok_cross": true,
        "ok_same": true,
        "leak": false,
        "count": 1,
        "msg": "duplicate key value violates unique constraint \"op_parent_unique_nik\"\nDETAIL:  Key (nik)=(3275260829010728) already exists.\n",
        "msg2": "duplicate key value violates unique constraint \"op_parent_unique_nik\"\nDETAIL:  Key (nik)=(3275260829010728) already exists.\n"
      },
      "before_state": null,
      "after_state": null,
      "notes": "G-010 ACCEPTED: global unique national NIK (no company_id on op.parent)"
    },
    {
      "case_id": "W02-PARENT-NIK-NO-LEAK-LIST",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "parents/list search by NIK",
      "expected": "no T2 parent identity leak",
      "actual": {
        "count": 0,
        "names": []
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-ARCHIVE-DEACTIVATE",
      "status": "PASS",
      "actor": "gt.school_admin",
      "precondition": null,
      "action": "students/set-active active=false",
      "expected": "student soft-deactivated; name intact",
      "actual": {
        "resp": {
          "success": true,
          "data": {
            "updated": 1,
            "failed": 0
          }
        },
        "before": {
          "active": "True",
          "name": "W02-CreateOK 10533Z"
        },
        "after": {
          "active": false,
          "name": "W02-CreateOK 10533Z"
        }
      },
      "before_state": null,
      "after_state": null,
      "notes": ""
    },
    {
      "case_id": "W02-STUDENT-SELF-SCOPE",
      "status": "PASS",
      "actor": "w02.self.10533z",
      "precondition": null,
      "action": "student portal must not see peer via admin/RPC",
      "expected": "denied/empty for peer",
      "actual": {
        "detail": "capability_denied",
        "list_error": "capability_denied",
        "rpc_exposed": false,
        "uid": 390
      },
      "before_state": null,
      "after_state": null,
      "notes": "ACCOUNT_LINK optional path only; disposable user deactivated after"
    }
  ]
}
