# AI Agent Instructions This file is the canonical instruction source for AI coding agents working on this repository. ## Core principles - Make focused, minimal diffs. - Preserve existing architecture and coding style. - Inspect nearby code and follow established patterns. - Prefer modern standards and APIs where they fit the existing codebase. - Do not rewrite whole files unless necessary. - Do not introduce unrelated formatting churn. - Present a plan and ask before making broad architectural changes. - When uncertain, ask for clarification before making changes. ## Project context ## EGroupware project context EGroupware is a large PHP/TypeScript/JavaScript web groupware application. The main repo contains many first-party apps such as `api`, `admin`, `calendar`, `addressbook`, `mail`, `filemanager`, `infolog`, `timesheet`, `resources`, `setup`, and others. Do not assume changes are isolated to one app without checking shared `api` and `setup` code. ### Repository shape - Backend code is primarily PHP. - Frontend code includes TypeScript, CSS, HTML, and build tooling. - Shared backend framework code lives under `api/`. - Frontend framework code lives under `kdots/` and `api/js/etemplate`. - Database setup and upgrade logic lives under `setup/` and app-specific setup directories. - Individual applications live in top-level directories such as `calendar/`, `addressbook/`, `mail/`, `infolog/`, and `timesheet/`. Primary expectations: - Respect EGroupware conventions. - Maintain backwards compatibility unless the task explicitly says otherwise. - Prefer incremental, reviewable changes. - Avoid speculative abstractions. - Develop a plan before making changes. - Keep UI, API, and database changes aligned with existing patterns, but mention improvements to match modern best practices. ## Code change rules - Read the relevant files first. - Search for existing implementations before adding new ones. - Identify the smallest safe change. - Keep diffs small and app-scoped when possible. - Before modifying app behaviour, check whether the pattern is implemented in another EGroupware app. Suggest refactoring when appropriate. - Avoid changing shared `api/` behaviour unless the task requires it. - For schema or setup changes, check app setup files and update paths. - Preserve backwards compatibility for existing installations. - Do not remove legacy compatibility code without explicit approval. - Check whether tests, migrations, translations, or documentation need updates. - Any new user-facing phrase (PHP `lang('...')`, JS `egw.lang('...')`, or `label=`/`value=`/`placeholder=`/etc. text in `.xet` templates) must be added to `$app/lang/egw_en.lang`, and translated in `$app/lang/egw_de.lang` when feasible. Files are tab-separated: `\t\t\t` (translation keeps original casing/punctuation). Check both `en` and `de` files before adding, to avoid duplicate keys. - `common` is a pseudo app-name (not a real app) for phrases needed everywhere - most of `api/lang/*` uses it, but ANY app's lang file can add a row tagged `common` too (eg. `mail/lang/egw_en.lang` has `mail\tcommon\ten\tMail` for the app's own display name, so other apps can show "Mail" without redefining it). Before adding a phrase, grep for it tagged `common` across all lang files (not just `api/lang/`) - skip adding it again if found, regardless of which file it lives in. - Determine the *correct* app-name by checking which apps' lang files are actually guaranteed to be loaded for the code path the phrase lives in, not just which directory the source file is in. Check every class involved for an explicit `Api\Translation::add_app(...)` in its constructor - both directions matter, not just the one you're editing. Example (verified both ways): `admin_mail` is also reachable via the mail app's account wizard (`mail_wizard extends admin_mail`), and `admin_mail::__construct()` force-loads `mail`'s lang file (`Api\Translation::add_app('mail')`) - but `mail_wizard::__construct()` *also* force-loads `admin`'s (`Api\Translation::add_app('admin')`), so for this particular pair both lang files are cross-loaded regardless of entry point, and a phrase used by either class can go in either `admin/lang/*` or `mail/lang/*`. Don't assume a one-directional gap from checking only the class you're touching - check the other side too. - When modifying a `.xet` file under an app's `templates/default/`, check for a `templates/mobile/` counterpart with the same template id (`