Scola V1 Production Baseline — Information Architecture Audit¶
Status: Proposed canonical IA for approval. Current navigation is inventory evidence, not approved product design. No source code was changed by this audit.
| Metadata | Value |
|---|---|
| Last verified | 2026-08-08 UTC |
| Mode / workspace | Server development, static read-only audit; /home/scola/odoo |
| Frontend baseline | scola-fe-v2 develop @ 68b77d88f78b3d32f0ede3a54b665e067b014062 |
| Backend baseline | custom_addons_scola/gcgscola main @ 2ff6ac7cb41810bbc3b66821cdad004b52d1658a |
| Product boundary | Proposed product contract |
| Release gates | QG-02 canonical IA, QG-03 authorization, QG-14 package separation, QG-18 freeze |
| Runtime limitation | Tenant module state, entitlement, effective permissions, served bundle, breadcrumbs, and actual click/403 behavior were not certified here |
Pedoman aktif yang dibaca: root dan frontend AGENTS.md, docs/ai-guidelines/AI_AGENT_MASTER_GUIDE.md, docs/ai-guidelines/development-guide.md, docs/ai-guidelines/workspace-governance.md, docs/ai-guidelines/architecture-api.md, docs/ai-guidelines/menu-architecture.md, docs/ai-guidelines/DOCUMENTATION_GOVERNANCE.md, serta seluruh evidence docs/recon/ dan docs/documentation-planning/ yang dirujuk. Delta backend e749832/2ff6ac7 mengeraskan ACL/elevation dan topology pins, tetapi tidak mengubah inventaris app/menu/route FE; tanpa bukti runtime, delta itu belum membuat QG-02 GREEN (../custom_addons_scola/gcgscola/scola_core/models/student_import_job.py:67-96; ../custom_addons_scola/gcgscola/scola_attendance/controllers/attendance_api_mixin.py:1407-1597; docs/documentation-planning/product-surface-candidates.md:420-430).
1. Executive finding and audit method¶
QG-02 is RED. Source saat ini mempunyai satu apps-based registry, tetapi belum merupakan satu product IA: 36 shell memuat 143 group dan 528 leaf; role registry memiliki 33 role; recon mencatat 1.003 effective route records (884 component pages, 119 redirect/alias); model statis menemukan 79 leaf yang dapat tampil tetapi ditolak route. Angka route dan denial berasal dari docs/recon/route-inventory.md:10-18 dan docs/recon/role-permission-matrix.md:983-1105. Current registry dan consumer contract berada di src/config/apps/index.js:1-100, src/config/apps/roleApps.js:1-951, dan src/composables/useApps.js:270-340,670-731.
Audit ini membedakan empat identitas yang tidak boleh dihitung sebagai empat produk:
- Product task — outcome yang disetujui dalam package/workflow contract.
- Canonical page — satu page identity dengan satu owner, walau dipakai beberapa role.
- Canonical route — URL utama untuk page identity itu.
- Compatibility route — alias/redirect sementara atau permanen yang tidak boleh menjadi menu, breadcrumb, analytics dimension, atau target dokumentasi terpisah.
Read-only current-code extraction mengonfirmasi 36 app, 143 group, 528 leaf, dan 33 ROLE_APPS entries. Dari leaf tersebut terdapat 36 route key berulang pada 122 leaf instances. Sebagian adalah reuse yang sah lintas role (/calendar, /news, employee self-service); sebagian adalah duplicate IA. Karena registry tidak menyimpan canonicalPageId atau alias owner, semuanya memerlukan klasifikasi eksplisit sebelum freeze. Status Confirmed from code hanya berlaku pada struktur source; impact tenant tetap Requires runtime verification.
1.1 Current navigation mechanics¶
| Layer | Current contract | Audit conclusion |
|---|---|---|
| App registry | APP_REGISTRY adalah ordered list global dan lookup by ID; consumers termasuk launcher/header/search (src/config/apps/index.js:1-100). |
Satu registry adalah fondasi yang benar, tetapi isinya belum mengikuti approved package boundary. |
| Role composition | ROLE_APPS mengatur app order, default/home, desktop/mobile mode, hidden nodes, pin, dan quick access (src/config/apps/roleApps.js:1-32,57-951). |
Role configuration adalah presentation composition, bukan authorization SSOT. |
| Route authority | Router memakai auth, capability, feature flag, allowed role, portal scope, dan admin-shell policy (src/router/routerGuardPolicy.js:1-220). |
Menu visibility dan route/API authority harus dihitung dari contract yang sama; current denials membuktikan drift. |
| Breadcrumb | useApps mencari longest route/context match dan menyusun App → Section → Leaf; detail tail ditambahkan terpisah (src/composables/useApps.js:285-340,670-731; src/composables/useBreadcrumbs.js:22-74; src/components/Topbar.vue:185-217). |
Hidden/direct/detail/alias route tidak otomatis mempunyai parent kanonis atau breadcrumb yang authorized. |
| Mobile | SSOT meminta empat pinned task + Menu dan folder drill-down (docs/ai-guidelines/menu-architecture.md:22-67). |
Specialist compact-role exception ada di config tetapi audit script tidak memodelkannya konsisten. |
2. Complete shell and menu-group disposition — 36 apps / 143 groups¶
Ledger ini mencakup setiap sections[]. Keep berarti candidate core group pada shell kanonis; entitle berarti pindahkan/pertahankan hanya dalam package yang aktif; withhold berarti tidak boleh muncul sampai product decision; remove berarti legacy/incomplete; internal berarti provider-only. Evidence per-group dan line source lengkap ada di docs/documentation-planning/product-surface-candidates.md:186-335; current app definitions berada di src/config/apps/appFragments/.
Current planning distribution adalah 13 core, 83 optional, 37 decision, 3 pilot, 4 incomplete, dan 3 internal groups. Proposed IA mengubah hanya analytics.executive dari decision menjadi legacy karena source sendiri menyebut app itu legacy (src/config/apps/appFragments/analyticsApp.js:1-6): hasil proposed disposition tetap tepat 143 = 13 keep + 83 entitle + 36 withhold + 3 pilot + 4 remove-incomplete + 3 internal + 1 remove-legacy.
| App shell (group count) | Keep in canonical core IA | Entitle / controlled pilot | Withhold, remove, or externalize | Evidence |
|---|---|---|---|---|
admin-dashboard (2) |
admin_dashboard.overview |
admin_dashboard.hr_personal (peer SC-PEOPLE/ATTEND+) |
— | src/config/apps/appFragments/adminDashboardApp.js:34-75 |
teacher-portal (9) |
teacher.today, teacher.school_calendar |
teacher.grades, teacher.digital_learning, teacher.kesiswaan, teacher.hr_personal, teacher.library |
Withhold mixed teacher.teaching, teacher.homeroom; split core leaves from package leaves |
src/config/apps/appFragments/teacherPortalApp.js:78-578 |
student-portal (7) |
student.school_calendar |
student.grades, student.discipline, student.library |
Withhold mixed student.today, student.learning, student.extras; rebuild each by package |
src/config/apps/appFragments/studentPortalApp.js:62-338 |
parent-portal (6) |
parent.dashboard |
parent.finance, parent.studentlife, parent.library |
Withhold mixed parent.academics, parent.communications; remove announcement leaf |
src/config/apps/appFragments/parentPortalApp.js:60-363 |
principal-exec (10) |
— | principal.finance_ops, principal.admissions, principal.library, principal.hr_personal only after entitlement and role approval |
Withhold principal.overview, principal.approvals_bucket, principal.academics, principal.people, principal.communication, principal.analytics |
src/config/apps/appFragments/principalApp.js:58-469 |
counselor (6) |
— | All six: counselor.overview, .students, .sessions, .pembinaan, .career, .hr_personal |
Keep SC-PEOPLE utility separate from sensitive SC-STUDENT workspace | src/config/apps/appFragments/counselorApp.js:58-304 |
extracurricular-coach (1) |
— | extracurricular_coach.overview |
Consolidate two competing leaves to list → detail → attendance hierarchy | src/config/apps/appFragments/extracurricularCoachApp.js:13-39 |
librarian (7) |
— | All seven: librarian.collection, .circulation, .textbook, .gls, .reports, .config, .attendance |
Move personal HR utility out of SC-LIB domain group | src/config/apps/appFragments/librarianApp.js:47-331 |
lab-technician (5) |
— | Externalize lab.hr_personal only if SC-PEOPLE/ATTEND+ permits |
Remove lab.overview, .inventory, .maintenance, .reports and shell until implemented |
src/config/apps/appFragments/labTechnicianApp.js:37-158; docs/recon/open-questions.md:44-54 |
security-officer (1) |
— | — | Withhold security.overview; no approved package/owner |
src/config/apps/appFragments/securityOfficerApp.js:22-40 |
school-committee (1) |
— | — | Withhold committee.overview; no approved package/aggregate audience |
src/config/apps/appFragments/schoolCommitteeApp.js:22-40 |
bos-admin (1) |
— | Pilot bos.overview |
Never leak into core finance/inventory IA | src/config/apps/appFragments/bosAdminApp.js:23-61 |
dapodik-operator (1) |
— | Pilot dapodik.overview |
Never leak into core settings/report-card IA | src/config/apps/appFragments/dapodikOperatorApp.js:24-55 |
dpo (1) |
— | — | Withhold dpo.overview pending privacy/compliance product contract |
src/config/apps/appFragments/dpoApp.js:23-52 |
foundation-auditor (1) |
— | foundation_auditor.overview |
Read-only scope must be proven | src/config/apps/appFragments/foundationAuditorApp.js:24-65 |
pickup-officer (1) |
— | — | Withhold pickup.tasks; pickup is not W05/manual attendance |
src/config/apps/appFragments/pickupOfficerApp.js:20-31 |
admin-staff (6) |
Move admin_staff.academics task to canonical Academic app |
admin_staff.admissions, .finance, .hr_personal after entitlement |
Withhold admin_staff.overview, .students until role/package scope closes |
src/config/apps/appFragments/adminStaffApp.js:43-243 |
foundation (6) |
— | All six foundation groups, with each peer finance/people/admission/attendance entitlement enforced | Generic foundation role itself remains withheld until catalog closure |
src/config/apps/appFragments/foundationApp.js:49-250 |
foundation-hr-ops (6) |
— | All six: foundation_hr.people, .structure, .operations, .leave, .compensation, .personal |
Split SC-PEOPLE, SC-ATTEND+, and transport entitlements | src/config/apps/appFragments/foundationHrOperationsApp.js:42-254 |
vp-curriculum (4) |
— | vp_curriculum.hr_personal after peer entitlement |
Withhold vp_curriculum.overview, .academics, .review; do not invent approval authority |
src/config/apps/appFragments/vicePrincipalApps.js:72-211 |
vp-student-affairs (5) |
— | vp_sa.guidance, .admissions, .hr_personal after role/package approval |
Withhold vp_sa.overview, .core |
src/config/apps/appFragments/vicePrincipalApps.js:225-398 |
vp-infrastructure (4) |
— | vp_inf.inventory, .hr_personal after role/package approval |
Withhold vp_inf.overview, .approval_maintenance |
src/config/apps/appFragments/vicePrincipalApps.js:409-508 |
vp-public-relations (4) |
Move vp_pr.communication tasks to canonical Communication app |
vp_pr.admissions, .hr_personal after entitlement |
Withhold vp_pr.overview |
src/config/apps/appFragments/vicePrincipalApps.js:525-602 |
vp-finance (3) |
— | vp_fin.core, .hr_personal after role/package approval |
Withhold vp_fin.overview |
src/config/apps/appFragments/vicePrincipalApps.js:616-686 |
academic (7) |
academic.master |
academic.assessment, .report_card, .lms, .cbt |
Split mixed academic.schedule; withhold legacy-vs-CBT academic.exam decision |
src/config/apps/appFragments/academicApp.js:51-377 |
student-affairs (2) |
student-affairs.data |
student-affairs.pembinaan |
— | src/config/apps/appFragments/studentAffairsApp.js:47-146 |
hr (3) |
— | hr.employees, hr.attendance, hr.leave under SC-PEOPLE/ATTEND+ |
— | src/config/apps/appFragments/hrApp.js:35-186 |
finance (8) |
— | finance.spp, .payables, .receivables, .cash, .reports, .config_assets, .hr_personal |
Withhold mixed finance.budget_approval; split SC-FIN/SC-BOS |
src/config/apps/appFragments/financeApp.js:61-452 |
inventory (7) |
— | inventory.master, .operations, .assets, .reports, .hr_personal; pilot inventory.budget |
Withhold inventory.procurement until SC-OPS/SC-BOS ownership closes |
src/config/apps/appFragments/inventoryApp.js:63-351 |
library (2) |
— | library.management, library.portal |
Merge page identities with librarian; do not create duplicate articles/routes |
src/config/apps/appFragments/libraryApp.js:47-253 |
admissions (2) |
— | admissions.pipeline, admissions.config |
Optional assessment/foundation/fees leaves need peer entitlement | src/config/apps/appFragments/admissionsApp.js:30-142 |
attendance (3) |
— | attendance.rfid, attendance.face.students only under SC-ATTEND+ |
Withhold mixed attendance.students until W05/pickup/settings split; W05 remains pilot |
src/config/apps/appFragments/attendanceApp.js:38-128 |
analytics (1) |
— | — | Remove legacy analytics.executive; link approved destinations from their owning domain |
src/config/apps/appFragments/analyticsApp.js:1-59; src/router/analyticsAdminRoutes.js:1-49 |
communication (5) |
communication.news, .calendar, .notifications |
— | Withhold communication.messaging, .complaints; remove legacy announcement page |
src/config/apps/appFragments/communicationApp.js:24-125 |
settings (2) |
settings.access |
— | Split mixed settings.school into core company settings plus package-specific configuration |
src/config/apps/appFragments/settingsApp.js:36-112 |
platform (3) |
— | — | Internal-only platform.ops, .tenants, .observability |
src/config/apps/appFragments/platformApp.js:26-132 |
3. Proposed canonical IA by role — 33/33 current role compositions¶
Ini adalah IA proposal, bukan grant. Supported di sini selalu berarti sesudah package, capability, record scope, tenant scope, domain owner, dan E2E disahkan. Role yang product contract-nya belum diputuskan diberi withhold, bukan diberi menu berdasarkan kode kebetulan. Current assignments/denials dapat diverifikasi di docs/recon/role-permission-matrix.md:983-1020; exact role composition ada di src/config/apps/roleApps.js:57-951.
| Current role | Proposed canonical navigation | Product disposition / release condition | Evidence |
|---|---|---|---|
admin |
Tidak menjadi school workflow persona; technical/break-glass entry terpisah dari school launcher | Internal/elevated contract must be approved; never use wildcard admin as ordinary E2E proof | src/config/apps/roleApps.js:57-104; src/config/roles.js:10-42 |
school_admin |
Home → Academic → Students → Attendance → Communication → Settings; optional apps appear only as separately entitled domains | Core V1 candidate; remove Analytics and all unavailable apps | src/config/apps/roleApps.js:105-152 |
teacher |
Section-first Teacher portal: Today, Calendar, Teaching core; optional Report, Learning, Student Services, Library, Personal as entitled folders | Core shell; W05 mutation remains controlled pilot | src/config/apps/roleApps.js:153-194 |
extracurricular_coach |
One SC-STUDENT workspace: list → activity detail → attendance sessions; account utility only | Optional Supported after duplicate/home fixes and assignment scope tests | src/config/apps/roleApps.js:195-211 |
student |
Student home with core Calendar/Schedule/Attendance; each optional package becomes a separate folder | Core portal; self-scope and enabled-package proof required | src/config/apps/roleApps.js:212-244 |
parent |
Parent home → child context → core schedule/attendance/news/calendar; finance/report/learning/student-service/library folders by entitlement | Core portal; every page must retain selected-child scope | src/config/apps/roleApps.js:245-263 |
principal |
Withhold current cross-domain shell; after decision, one executive read/approval inbox whose actions link to owning domain | Requires Product Decision; no implicit approve/edit rights | src/config/apps/roleApps.js:264-315 |
platform_operator |
Platform Operations → Tenants → Observability; no school-user domain apps | Internal Platform and separate shell/audit session | src/config/apps/roleApps.js:316-334 |
admin_staff |
Withhold synthetic mixed shell; approve task set, then reuse canonical Academic/Student/Admissions/Fees pages | Requires Product Decision and route parity | src/config/apps/roleApps.js:335-361 |
pickup_officer |
Withhold; if approved later, dedicated pickup/transport task shell, never Attendance W05 | Requires Product Decision/privacy/child-release owner | src/config/apps/roleApps.js:362-372 |
counselor |
Sensitive SC-STUDENT workspace: caseload → session → follow-up; optional Library/Personal utilities separated | Optional Supported only after least-privilege/DPO closure | src/config/apps/roleApps.js:373-403 |
librarian |
Library dashboard → Collection → Circulation → Textbook → GLS → Reports → Configuration; account utility outside domain | Optional Supported; one canonical Library page family | src/config/apps/roleApps.js:404-431 |
lab_technician |
No customer launcher until pages are implemented; optional employee self-service accessed from account shell only | Incomplete / Remove from Product Surface | src/config/apps/roleApps.js:432-460; docs/recon/open-questions.md:44-54 |
dpo |
Withhold until audit/consent purpose, data classes, retention, export, and authority are approved | Requires Product Decision | src/config/apps/roleApps.js:461-475 |
security_officer |
Withhold until visitor/security package and school scope are approved | Requires Product Decision | src/config/apps/roleApps.js:476-486 |
school_committee |
Withhold until aggregate audience and confidentiality contract are approved | Requires Product Decision | src/config/apps/roleApps.js:487-497 |
bos_admin |
Dedicated BOS pilot: Dashboard → Budget/RKAS → Procurement → Ledger/Reports; no general Finance app | Controlled Pilot; SC-FIN + SC-OPS and maker-checker gates | src/config/apps/roleApps.js:498-517 |
dapodik_operator |
Dedicated Dapodik pilot: Status → Preview → Sync → Conflict/Recovery; related report/attendance links only when companions enabled | Controlled Pilot with kill switch and tenant mapping | src/config/apps/roleApps.js:518-536 |
foundation_auditor |
Read-only Foundation audit workspace; no mutation controls | Optional Supported after cross-company/foundation scope proof | src/config/apps/roleApps.js:537-556 |
inventory_officer |
SC-OPS Inventory app only; Finance links only for an approved integration task; personal utility outside domain | Optional Supported; BOS nodes pilot-only | src/config/apps/roleApps.js:557-588 |
foundation |
Withhold generic role; use an approved canonical foundation role instead | Requires role-catalog Product Decision despite optional foundation app | src/config/apps/roleApps.js:589-620; ../custom_addons_scola/gcgscola/scola_core/services/role_catalog.py:7-40 |
foundation_hr_admin |
Foundation HR: People → Structure → Leave → Compensation; Attendance/Transport only by peer entitlement | Optional Supported after one-role/one-scope closure | src/config/apps/roleApps.js:621-649 |
foundation_chairman |
Foundation executive read/approval shell with separately entitled domains; remove legacy Analytics shortcut | Optional Supported after approval matrix and company aggregation proof | src/config/apps/roleApps.js:650-682 |
head_admin |
Withhold current mixed assignment; after role decision use task-scoped school operations IA and canonical domain pages | Requires role/route parity; 15 modeled denials cannot be normalized | src/config/apps/roleApps.js:683-717; docs/recon/role-permission-matrix.md:1008 |
treasurer |
Finance app with Fees/Accounting split; BOS only when pilot enabled; account utility outside domain | Optional Supported; post/reconcile/approve separation required | src/config/apps/roleApps.js:718-753 |
homeroom |
Reuse Teacher portal with homeroom section enabled by dated class assignment; no parallel homeroom app | Core shell plus entitled optional tasks | src/config/apps/roleApps.js:754-793 |
alumni |
Withhold current Student/Library/Communication composition; if supported, minimal historical self-service portal | Requires Product/Privacy Decision; two modeled denials and retention scope | src/config/apps/roleApps.js:794-834; docs/recon/role-permission-matrix.md:1011 |
pendaftar |
Standalone /spmb/* journey; no authenticated apps launcher |
Optional SC-ADM portal with explicit public/auth boundary | src/config/apps/roleApps.js:835-840; src/router/routerGuardPolicy.js:49-82 |
vice_principal_curriculum |
Withhold shell; after decision, curriculum overview/review pages only, linking to owning Academic/Report domains | Requires Product Decision and approval matrix | src/config/apps/roleApps.js:841-860 |
vice_principal_student_affairs |
Withhold shell; after decision, student-affairs oversight with explicit read/approve/correct split | Requires Product Decision; current communication denials | src/config/apps/roleApps.js:861-888 |
vice_principal_infrastructure |
Withhold shell; after decision, SC-OPS oversight pages only | Requires Product Decision; current 27-denial cluster blocks release | src/config/apps/roleApps.js:889-911; docs/recon/role-permission-matrix.md:1014 |
vice_principal_public_relations |
Withhold shell; if approved, canonical Communication tasks plus separately entitled Admissions outreach | Requires Product Decision; do not duplicate domain pages | src/config/apps/roleApps.js:912-933 |
vice_principal_finance |
Withhold shell; if approved, Finance oversight/read/approval tasks with explicit action capability | Requires Product Decision | src/config/apps/roleApps.js:934-951 |
cashier is not a 34th role contract: it occurs in a finance route allowlist but is absent from ROLES, ROLE_APPS, and the backend role catalog (src/router/financeRoutes.js:124; src/config/roles.js:10-42; ../custom_addons_scola/gcgscola/scola_core/services/role_catalog.py:7-40). It must be removed as stale or formally added by Product/Security before any IA assignment.
4. Proposed Scola IA contract¶
4.1 Shell and package boundary¶
- One customer shell per active role context, with one canonical home that resolves. Apps represent approved product domains, not arbitrary component folders.
- Core apps appear first: Home, Academic, Students, Attendance, Communication, Settings. Optional apps appear only when installed and entitled and authorized; pilots have a persistent pilot label and tenant/user allowlist. Internal, legacy, incomplete, and undecided apps are absent from launcher, sidebar, search, recents, favorites, deep links, breadcrumbs, and notifications.
- Employee self-service is an account utility reused by eligible staff roles, not copied into every domain section. Cross-domain approvals live in an approval inbox only after each action delegates to the owning domain capability/API.
- A hidden route is not a security control. Direct navigation must apply the same package, capability, record, tenant, and domain-owner contract as its menu node.
4.2 Page, route, alias, and duplicate contract¶
| Surface family | Proposed canonical treatment | Compatibility disposition | Evidence |
|---|---|---|---|
| Teacher home | /faculty/dashboard |
/faculty/home redirect only; never use as home/menu/breadcrumb |
src/router/teacherRouteFragments/core.js:22-30; src/config/apps/appFragments/teacherPortalApp.js:47-81 |
| Grade level | /grade-levels |
/course-level alias retained only if migration evidence requires it |
src/router/academicCoreRoutes.js:22-27; current menu alias src/config/apps/appFragments/academicApp.js:51-75 |
| News | /news and authorized admin publication routes |
/announcement page/model is deprecated; migrate or remove, not a competing page |
src/router/sharedExperienceRoutes.js:57-66; docs/recon/open-questions.md:39-40 |
| Analytics shortcuts | Owning Principal/VP/domain route | Remove /admin/analytics/* menu/app; compatibility redirects get owner and expiry |
src/router/analyticsAdminRoutes.js:1-49 |
| Extracurricular | /faculty/extracurricular list; child /:id/absensi* for attendance |
Remove the second same-route leaf | src/config/apps/appFragments/extracurricularCoachApp.js:19-37; src/router/counselingRoutes.js:578-622 |
| Library management | One canonical /library/* domain family (exact migration list requires route decision) |
/librarian/* and /config/* mirrors become scoped redirects only when they do not change scope |
src/router/libraryConfigRoutes.js:1-55; src/router/libraryManagementRoutes.js:270-274; src/router/librarianRoutes.js:144-149 |
| Shared calendar/news/self-service | One canonical page identity may be referenced by many role menus | Same route reuse is intentional only when component, capability, scope, and terminology are identical | src/composables/useApps.js:285-340; duplicate inventory requires runtime closure |
| All remaining aliases | Canonical route declared in route metadata/registry | Each of 119 records gets canonical target, owner, reason, telemetry, test, and expiry/permanent rationale | docs/recon/route-inventory.md:1293-1415 |
Canonical technical route/name convention is English kebab-case; visible terminology is approved Bahasa Indonesia and comes from one label/glossary registry. Existing mixes such as Analytics, Academic Governance, Admission Terpusat, Absensi, and Kehadiran are not silently normalized here; Product/School SME must approve the term, then menu, page title, breadcrumb, status, and help key use the same value (src/config/apps/appFragments/analyticsApp.js:13-20; src/config/apps/appFragments/vicePrincipalApps.js:107-158; src/config/apps/appFragments/foundationApp.js:205-222; docs/ai-guidelines/AI_AGENT_MASTER_GUIDE.md:103-105,267).
4.3 Breadcrumb, dashboard, and entry-point contract¶
- Every supported page has
canonicalPageId, owning app/group, canonical route, page title, allowed role/capability/scope, package gate, and related/detail route patterns. - Breadcrumb is generated only from authorized ancestors and always resolves to canonical routes. Detail/create/edit/import pages inherit the list/workspace parent; alias URLs replace browser history with their canonical target.
- Dashboard cards, global search, notifications, recents, favorites, contextual links, and mobile pins are all entry points and use the same resolver as the menu. No card may link to a route omitted/forbidden by the role IA.
- A supported role has exactly one resolvable home. Failure to resolve is an explicit access/configuration error, not fallback to
/, an unrelated dashboard, or technical admin. - Empty/incomplete shells return an approved unavailable boundary only for sold-but-disabled packages; placeholders and removed products return not-found/unsupported and are not discoverable.
4.4 Menu-to-authority contract¶
Rendered navigation is the intersection of approved product status + installed addon + entitlement + active-role capability + record scope + tenant/company scope + domain ownership. Router and API must independently deny everything outside that intersection. This makes menu hiding a usability outcome, not an authorization mechanism. The active-role and deny-by-default semantics are owned by the authorization plan; QG-02 cannot pass while QG-03 is RED.
5. IA finding register¶
Severity menunjukkan impact bila surface tetap masuk baseline. Item Decision bukan ordinary bug: Product/Security/SME harus menetapkan contract sebelum engineering dapat menutupnya. Disposition removal/hide adalah preferred default ketika mempertahankan surface hanya menambah accidental complexity.
| ID | Severity / type | Finding and affected surface | Proposed disposition / acceptance | Exact evidence |
|---|---|---|---|---|
IA-001 |
Release blocker | Core, optional, pilot, internal, and undecided leaves coexist in the same launcher/section; menu existence can imply unsupported product availability. | Approve package ownership per leaf; split groups; unavailable leaves absent from every entry point. | docs/documentation-planning/product-surface-candidates.md:186-335; src/config/apps/index.js:45-94 |
IA-002 |
Release blocker — Decision | Role catalogs are not one set: FE ROLES omits security_officer, school_committee, bos_admin, and generic foundation; backend has 32 roles without foundation; route admits orphan cashier. |
Approve one versioned role catalog; remove stale aliases or add full contract; contract test exact equality. | src/config/roles.js:10-42; src/config/apps/roleApps.js:57-951; ../custom_addons_scola/gcgscola/scola_core/services/role_catalog.py:7-40; src/router/financeRoutes.js:124 |
IA-003 |
Release blocker | Eight configured homes do not resolve: five /vice-principal/*/dashboard, /finance/dashboard, /library/dashboard, and /faculty/extracurricular/dashboard. |
Point each role/app to an existing canonical home; route/menu/home contract test for all 33 roles. | src/config/roles.js:43-77; src/config/apps/roleApps.js:195-203; canonical VP homes src/config/apps/appFragments/vicePrincipalApps.js:78,232,416,532,623; current inventory docs/recon/route-inventory.md:10-18 |
IA-004 |
Release blocker | Static role model finds 79 visible-but-forbidden leaves; a menu can advertise an action that route guard rejects. | Zero visible-forbidden leaf for supported runtime account/module combinations; unauthorized direct route still denied. | docs/recon/role-permission-matrix.md:983-1105 |
IA-005 |
Major consistency issue | analytics is explicitly a legacy shortcut app, still competes with principal/VP/domain/platform routes through redirects. |
Remove app from supported role configs; retain only measured compatibility redirects with owner/expiry. | src/config/apps/appFragments/analyticsApp.js:1-59; src/router/analyticsAdminRoutes.js:1-49 |
IA-006 |
Release blocker | Lab shell exposes dashboard plus six literal placeholder child pages; optional HR utilities make incomplete shell appear usable. | Remove shell/routes/menu from customer surface; reintroduce only with package/API/E2E contract. | docs/recon/open-questions.md:44-54; src/views/Laboran/Inventory/LabInventoryList.vue:1-12; src/config/apps/appFragments/labTechnicianApp.js:37-158 |
IA-007 |
Release blocker | Public /register renders only a Register heading and competes with implemented SPMB registration. |
Remove generic route or redirect only after an explicit acquisition-flow decision; canonical SPMB register remains SC-ADM. | src/router/sharedExperienceRoutes.js:1-25; src/views/Register.vue:1-7; src/router/spmbRoutes.js:45-55 |
IA-008 |
Major consistency issue | Teacher app/role home uses compatibility /faculty/home while canonical component route is /faculty/dashboard. |
Canonicalize every home/menu/breadcrumb to /faculty/dashboard; retain redirect only for compatibility. |
src/router/teacherRouteFragments/core.js:22-30; src/config/apps/appFragments/teacherPortalApp.js:47-81; src/config/roles.js:61-62 |
IA-009 |
Major consistency issue | “Daftar Ekskul” and “Absensi Ekskul” are two leaf labels/capabilities pointing to the same list route, while actual attendance pages are children. | Keep one list leaf; put attendance beneath selected extracurricular detail; manage capability on mutation routes/API. | src/config/apps/appFragments/extracurricularCoachApp.js:19-37; src/router/counselingRoutes.js:578-622 |
IA-010 |
Release blocker | Canonical scola.news and parallel /announcement page/model coexist; parent communication advertises the legacy concept. |
Remove announcement from menus/search; migrate or deprecate data; one canonical News identity. | src/router/sharedExperienceRoutes.js:57-66; src/config/apps/appFragments/parentPortalApp.js:257-315; docs/documentation-planning/documentation-blockers.md:478-488 |
IA-011 |
Release blocker | Settings school group mixes core company/capacity with attendance, LMS, and Dapodik config; visible root does not prove every package/manage authority. | Split settings by owning package and configure capability; core Settings contains only approved core configuration/access. | src/config/apps/appFragments/settingsApp.js:20-112; docs/documentation-planning/documentation-blockers.md:272-282 |
IA-012 |
Release blocker — Decision | Attendance shell mixes manual student attendance/W05, pickup, settings, RFID, and face recognition despite different packages, actors, privacy bases, and maturity. | Separate Core read hub, controlled W05 pilot, SC-ATTEND+ app, and pickup decision surface. | src/config/apps/appFragments/attendanceApp.js:22-128; docs/documentation-planning/pilot-attendance-plan.md:15-30,317-343 |
IA-013 |
Major consistency issue | Student/parent groups mix core timetable/attendance/news with Fees, Report, Learning, Assessment, Student Services, Library, and analytics. | Preserve role shell but split package folders; absent package leaves disappear without leaving empty group. | src/config/apps/appFragments/studentPortalApp.js:62-338; src/config/apps/appFragments/parentPortalApp.js:60-363 |
IA-014 |
Release blocker — Decision | Principal shell is a cross-domain dashboard, mutation/approval bucket, analytics, and personal utility without approved leadership SKU or action ownership. | Withhold; approve read/approve task matrix, then link to owning domains instead of copying full apps. | src/config/apps/appFragments/principalApp.js:58-469; docs/recon/open-questions.md:35-42 |
IA-015 |
Release blocker — Decision | Five VP shells use mixed English/Indonesian names, configured /dashboard homes that do not exist, shared components, optional-domain duplication, and unapproved approval semantics. |
Approve each role’s task contract or consolidate into one leadership shell; canonical routes reuse owning page identity. | src/config/apps/appFragments/vicePrincipalApps.js:72-686; src/config/roles.js:50-54 |
IA-016 |
Release blocker — Decision | Technical admin and school school_admin receive nearly the same broad domain launcher, while backend labels admin as technical; wildcard authority can mask ordinary-role defects. |
Separate technical/break-glass IA from school administration; never certify school workflow with admin. |
src/config/apps/roleApps.js:57-152; ../custom_addons_scola/gcgscola/scola_core/services/role_catalog.py:7-10; src/router/routerGuardPolicy.js:18-31,151-179 |
IA-017 |
Release blocker — Decision | head_admin is assigned 12 mixed apps but access filtering leaves a different subset and 15 denied leaves. |
Approve Kepala TU task/package contract; use canonical domain pages; zero modeled/runtime contradictions. | src/config/apps/roleApps.js:683-717; docs/recon/role-permission-matrix.md:1008,1025-1105 |
IA-018 |
Release blocker — Decision | VP infrastructure receives both a VP shell and Inventory/HR/Communication, but 27 modeled leaves are forbidden—the largest denial cluster. | Withhold role navigation until SC-OPS read/approve scope is approved and route/API parity tests pass. | src/config/apps/roleApps.js:889-911; docs/recon/role-permission-matrix.md:1014,1060-1105 |
IA-019 |
Major consistency issue | Auto breadcrumb depends on visible menu route matching; hidden/direct/detail/import pages can lack an authorized canonical ancestor, and duplicate routes can pick role-preferred context. | Add canonical page owner and related-route metadata; crawl every supported route/role and assert breadcrumb targets resolve and authorize. | src/composables/useApps.js:285-340,670-731; src/composables/useBreadcrumbs.js:22-74; src/components/Topbar.vue:185-217 |
IA-020 |
Major consistency issue | Current registry has 36 repeated route keys across 122 leaf instances; reuse and accidental duplication are not distinguished in schema. | Add canonicalPageId/intentional-reuse allowlist; one search/result/help/analytics identity per page; zero unclassified duplicates. |
src/config/apps/appSchema.js:1-74; src/config/apps/index.js:45-100; static current-code extraction in §1 |
IA-021 |
Major consistency issue | 119 redirect/alias records lack a single lifecycle/owner contract and can be counted as pages or chosen by menu/home. | Inventory canonical target, owner, reason, telemetry, test, and expiry/permanent rationale; no alias in canonical UI. | docs/recon/route-inventory.md:1293-1415 |
IA-022 |
Major consistency issue | npm run audit:menu currently reports false missing routes for template-generated Library config and grade-level alias; it also hardcodes compact exceptions and exact pin counts inconsistent with compactPortal. |
Repair audit before making it a gate; parse effective router, use navigation metadata, and add fixtures for generated/alias routes. | package.json:10; scripts/audit-menu-ia.mjs:8-39,122-205; src/router/libraryConfigRoutes.js:1-55; src/config/apps/roleApps.js:25-28,195-203 |
IA-023 |
Release blocker | UI/package gates are not proven as one contract; route metadata includes secondaryFeatureFlag, while the router policy only evaluates featureFlag, and feature aliases include non-catalog flags. |
One resolver for primary+secondary package requirements across app/menu/route/API; all optional combinations negative-tested. | src/router/foundationRoutes.js:93-103,163-174; src/router/routerGuardPolicy.js:350-394,504-519; src/config/featureFlagFragments/moduleFeatureFlags.js:1-58; docs/modular/product-tier-and-feature-flags.md:19-31 |
IA-024 |
Release blocker | /students/create and /students/import are direct-routable using students.list.view; menu hiding/import UX cannot replace create/import authority. |
Define create/import capabilities, route guards, API guards, package/scope parity, and negative direct-route tests. | src/router/studentDataAdminRoutes.js:14-44; docs/documentation-planning/documentation-blockers.md:382-392 |
IA-025 |
Release blocker | Principal/VP attendance pages can expose Create/Pending mutation CTA while backend treats those actors as read-only. | Hide unauthorized actions and keep mutation API denied, or approve/implement a different role contract; test menu → route → API parity. | src/views/AttendanceManagement/Admin/AttendanceSheetList.vue:9-20,192; src/router/principalRoutes.js:196-218; src/router/vicePrincipalRoutes.js:214-231; docs/documentation-planning/documentation-blockers.md:394-404 |
IA-026 |
Release blocker | Counts and source consistency are static; no named tenant/build has proven homes, dead links, hidden-route denial, optional-module combinations, breadcrumbs, responsive launcher, or served-bundle parity. | Execute canonical IA crawl on Golden Tenant for every supported role and module matrix; record exact FE/BE/DB/module build. | docs/documentation-planning/documentation-blockers.md:80-90; QG-02/QG-18 |
No current inconsistency was accepted as an Intentional domain-specific difference without an explicit owner/rationale. Candidate intentional reuse (calendar/news/self-service) remains subject to the IA-020 allowlist and runtime scope proof.
6. Audit coverage and closure tests¶
| Requested audit dimension | Coverage/result | Closure evidence required |
|---|---|---|
| Application shells | All 36 covered in §2; proposed app status remains in 01-product-contract.md |
Approved allowlist; unsupported shell absent; app home resolves |
| Role dashboards | All 33 role compositions covered in §3; eight invalid homes in IA-003 |
Fresh account lands on authorized canonical home for each supported role |
| Menus/submenus | All 143 groups covered in §2; 528 current leaf total; mixed package issues IA-001, 011–015 |
Registry snapshot equals approved product/package manifest |
| Page and route names | 884 component pages remain the exhaustive recon inventory; this audit establishes page identity/canonical route contract, not support for all pages | Every routed component tagged supported, direct child, compatibility, internal, or remove; no unclassified page |
| Breadcrumbs | Auto lineage and dynamic tail audited in IA-019 |
Role-aware crawl proves every supported page has authorized canonical parent/back target |
| Aliases | All 119 recon redirect/alias records covered by IA-021; key families in §4.2 |
Canonical target/owner/reason/expiry registry; aliases absent from UI/search/help |
| Duplicate/shared pages | 36 repeated route keys / 122 leaf instances covered by IA-020; library/extracurricular examples in §4.2 |
canonicalPageId and intentional-reuse allowlist; same component is not counted twice |
| Legacy pages | Analytics and Announcement covered by IA-005/IA-010 |
Removed from role configs and all entry points; migration/redirect tests only if retained |
| Placeholders | Lab and /register covered by IA-006/IA-007 |
Zero reachable placeholder on supported role/module matrix |
| Dead links | Known eight invalid homes in IA-003; current audit-tool limitation in IA-022 |
Effective-router crawler covers literal, generated, alias, param, card, notification, and breadcrumb links |
| Hidden-but-routable pages | Direct import/create in IA-024; 884 pages versus 528 leaves shows that no-menu pages need classification, not automatic removal |
Direct route positive/negative tests and canonical parent for every supported hidden child |
| Visible-but-forbidden pages | 79 modeled leaves in IA-004, including IA-017/IA-018 clusters and IA-025 CTA conflict |
Zero supported menu/card/action contradiction with route/API for fresh accounts |
| Optional-module leakage | IA-001, IA-011–IA-013, and IA-023 |
Pairwise and dependency module/entitlement matrix; no empty or leaked group |
| Navigation differences by role | §3 covers 33/33; differences are task/scope/package-driven | Product-approved rationale for each role-specific variant; otherwise consolidate |
6.1 QG-02 measurable acceptance¶
QG-02 can become GREEN only when one exact Golden Tenant release candidate proves all of the following:
- Approved app/group/leaf manifest contains only Core V1 plus enabled Optional Supported/Controlled Pilot nodes; legacy, internal, incomplete, and undecided nodes have zero discoverability.
- All supported fresh-role accounts resolve one canonical home; zero fallback to
/, technical admin, missing route, unrelated domain, or forbidden route. - Every menu, submenu, launcher folder, dashboard card, mobile pin, quick action, notification link, search result, favorite, recent item, breadcrumb, and contextual link resolves to an authorized canonical route.
- Every supported direct route passes its own package/capability/scope checks; the same negative matrix returns the approved deny/not-found response for unauthorized, out-of-assignment, and cross-tenant actors.
- Alias crawl reports 119 or an approved reduced count, with zero alias used as a current menu/home/breadcrumb target and zero redirect loop.
- Duplicate report contains zero unclassified route/component/page identity; allowed reuse has identical authority/scope or an explicit role-context reason.
- Effective router audit understands generated routes and
compactPortal;npm run audit:menuand contract tests pass without regex false positives (scripts/audit-menu-ia.mjs:23-39,122-205). - Desktop and 320/360/390 px mobile navigation expose the same task contract, with four task pins plus Menu except an approved compact-role rule represented in both runtime and audit metadata.
- Runtime evidence records role, active role, assigned roles, permissions, tenant/company, installed modules, entitlement flags, route, API outcome, FE/BE/database/module build, and served asset hash.
7. Decision ownership and implementation boundary¶
| Decision class | Accountable owner | Engineering may do before decision | Engineering must not infer |
|---|---|---|---|
| Product/package boundary | Product + Commercial | Hide/remove clearly legacy/incomplete/internal surfaces; prepare manifest | That coded module/menu is sold/supported |
| Role/task IA | Product + Security + School SME | Fix confirmed broken links/aliases that preserve approved outcome | Leadership/admin approval authority or sensitive-data access |
| Privacy-sensitive navigation | DPO + Security + domain owner | Fail closed and minimize discoverability | Counseling, biometric, proof, alumni, DPO, committee, pickup audience |
| Compatibility/deprecation | Product + Operations | Measure route use and add redirect tests | Permanent support merely because an alias exists |
| Terminology | Product + School SME; DPO for identifiers/status | Centralize approved label after decision | Whether “Absensi”, “Kehadiran”, “Sakit”, “Izin”, or English domain terms are equivalent |
8. Self-review¶
- Unsupported claims: all proposed role/menu structures are explicitly proposals; no static result is described as runtime production behavior.
- Duplicated surfaces: all 36 shells, 143 groups, 119 aliases, repeated leaf routes, Library mirrors, Analytics redirects, News/Announcement, and extracurricular duplicate are assigned a disposition.
- Unresolved contradictions: role catalogs, eight homes, 79 denials, settings/attendance/package mixing, and audit-script drift remain visible blocker IDs rather than normalized behavior.
- Security boundaries: menu hiding is never treated as authorization; direct routes, active role, capability, record/assignment, tenant/company, sensitive data, and domain ownership are release criteria.
- E2E links: IA gate explicitly covers launcher/menu/card/search/notification/breadcrumb → route → API and cross-module combinations; detailed workflows/tests remain owned by the workflow catalog and release matrix.
- Decision hygiene: principal/VP/head-admin/admin-staff/foundation/alumni/DPO/security/committee/pickup/cashier decisions are not disguised as engineering bugs; removal/withhold is the default until approved.
Until QG-01, QG-02, QG-03, QG-14, QG-16, and QG-18 pass together, this document is an auditable stabilization proposal—not a frozen IA and not a source for user documentation.