{
  "version": "2.0.0",
  "guards": [
    {
      "id": "GR-CORE-003",
      "area": "core",
      "reviewer": "code",
      "title": "Request input contract",
      "requirement": "MUST read input via requestInput() in pages and avoid superglobals for request data."
    },
    {
      "id": "GR-CORE-007",
      "area": "core",
      "reviewer": "code",
      "title": "OpenAPI sync",
      "requirement": "MUST update docs/openapi.yaml in the same merge when API endpoints change."
    },
    {
      "id": "GR-CORE-010",
      "area": "core",
      "reviewer": "code",
      "title": "Idempotent DB update scripts",
      "requirement": "Schema or data changes for existing installs MUST be shipped as idempotent SQL scripts in db/updates/ (IF NOT EXISTS, ON DUPLICATE KEY, guarded ALTER TABLE). db/init/init.sql alone is not sufficient."
    },
    {
      "id": "GR-CORE-011",
      "area": "core",
      "reviewer": "code",
      "title": "Settings in DB, not config files",
      "requirement": "New application settings MUST be stored in the settings DB table. config/settings.php is a partial hot-path cache only."
    },
    {
      "id": "GR-CORE-012",
      "area": "core",
      "reviewer": "code",
      "title": "POST-Redirect-GET",
      "requirement": "Actions that successfully mutate state MUST respond with a redirect (PRG pattern). Flash messages set before redirect via Flash::set()."
    },
    {
      "id": "GR-TEST-001",
      "area": "test",
      "reviewer": "code",
      "title": "Tests ship with new logic",
      "requirement": "New or changed business logic in lib/Service/ MUST be accompanied by PHPUnit tests covering the happy path and at least one failure or edge case. No new public method in a Service or Gateway without a test."
    },
    {
      "id": "GR-TEST-002",
      "area": "test",
      "reviewer": "code",
      "title": "Code must be testable",
      "requirement": "New code MUST use constructor injection. MUST NOT introduce static side-effects in constructors, hidden global state, or direct DB/HTTP calls outside Repository/Gateway abstractions."
    },
    {
      "id": "GR-UI-014",
      "area": "ui",
      "reviewer": "code",
      "title": "UI state completeness",
      "requirement": "New user-facing views or components MUST handle four states: loading, empty, error, success. All four defined in plan before implementation. Not required for small fixes to existing UI."
    },
    {
      "id": "GR-UI-LIST",
      "area": "ui",
      "reviewer": "code",
      "title": "List and grid standards",
      "requirement": "Lists MUST preserve row action column plus enter/dblclick fallback behavior. Page size options and URL/localStorage/default precedence MUST be maintained. New lists MUST use initStandardListPage()."
    },
    {
      "id": "GR-UI-DETAIL",
      "area": "ui",
      "reviewer": "code",
      "title": "Detail page standards",
      "requirement": "MUST use standard detail page contracts for dirty-state and actions. MUST NOT use inline confirm handlers on standardized flows. MUST use shared list/detail partials for standardized UI blocks. New pages MUST use the established layout system. Blocks reused in 2+ pages MUST be extracted as partials (app-{context}-{purpose}.phtml)."
    },
    {
      "id": "GR-UI-A11Y",
      "area": "ui",
      "reviewer": "code",
      "title": "Accessibility",
      "requirement": "MUST NOT regress a11y basics (labels, roles, keyboard focus) on touched flows. New interactive components MUST use semantic HTML, be keyboard-operable (Tab/Enter/Escape), use ARIA only where necessary, and meet WCAG 2.1 AA contrast."
    },
    {
      "id": "GR-UI-I18N",
      "area": "ui",
      "reviewer": "code",
      "title": "Internationalization completeness",
      "requirement": "All user-visible strings in phtml views MUST use t('key'). Every new t() key MUST be present in ALL language files (i18n/default_de.json and i18n/default_en.json) in the same merge."
    },
    {
      "id": "GR-UI-REUSE",
      "area": "ui",
      "reviewer": "code",
      "title": "Reuse-first (JS + CSS)",
      "requirement": "MUST check web/js/core/ and web/js/components/ for existing exports before adding new functions. MUST check existing CSS layers and app-* classes before writing new rules. No duplication of logic from core modules."
    },
    {
      "id": "GR-SEC-001",
      "area": "security",
      "reviewer": "security",
      "title": "CSRF on POST",
      "requirement": "MUST verify CSRF token on every POST endpoint in pages before processing the request body."
    },
    {
      "id": "GR-SEC-002",
      "area": "security",
      "reviewer": "security",
      "title": "No PII/secrets in logs",
      "requirement": "MUST NOT write PII (email, names, UUIDs) or secrets (tokens, passwords) to logs or audit metadata without prior redaction."
    },
    {
      "id": "GR-SEC-003",
      "area": "security",
      "reviewer": "security",
      "title": "SQL via prepared statements only",
      "requirement": "MUST NOT build SQL strings with user-controlled input. All queries MUST go through Repository classes using prepared statements."
    },
    {
      "id": "GR-SEC-004",
      "area": "security",
      "reviewer": "security",
      "title": "No external CDN/remote assets",
      "requirement": "MUST NOT load JS, CSS, fonts from external CDNs or remote URLs. Third-party libraries MUST be vendored locally and served via assetVersion()."
    },
    {
      "id": "GR-SEC-005",
      "area": "security",
      "reviewer": "security",
      "title": "Encryption via Crypto only",
      "requirement": "Encryption/decryption MUST use lib/Support/Crypto.php (AES-256-GCM). No raw openssl_encrypt, no base64 as encryption, no hand-rolled crypto."
    },
    {
      "id": "GR-SEC-006",
      "area": "security",
      "reviewer": "security",
      "title": "File uploads in storage/ only",
      "requirement": "User-uploaded files MUST be stored under storage/ with non-guessable identifiers. MUST NOT be written to web/. MUST be served via PHP controller with authz. MUST validate MIME + extension before storing."
    },
    {
      "id": "GR-SEC-007",
      "area": "security",
      "reviewer": "security",
      "title": "API must not start sessions",
      "requirement": "API requests (api/ prefix) MUST NOT trigger Session::start(), remember-me cookie handling, or session timeout redirects."
    },
    {
      "id": "GR-SEC-008",
      "area": "security",
      "reviewer": "security",
      "title": "Server-side authorization",
      "requirement": "MUST enforce authorization server-side in actions and services. UI-only permission checks are insufficient."
    },
    {
      "id": "GR-SEC-009",
      "area": "security",
      "reviewer": "security",
      "title": "Server-side tenant scope",
      "requirement": "MUST enforce tenant scope server-side. Every query touching tenant-scoped data MUST filter by tenant_id. No cross-tenant data leakage."
    }
  ]
}
