Replace prominent outlined chevron buttons with borderless ghost icons
and gradient fade masks for a cleaner overflow indicator. Reset shell-level
button custom properties to prevent global button styles from bleeding through.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inconsistent `primary` class with `app-action-success` on all
standalone save buttons so they match the green titlebar convention.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create typography.tokens.css as single source of truth for the type system:
- Extract all primitive tokens from variables.base.css (--text-*, --leading-*, --font-*, --tracking-*)
- Add semantic aliases (--text-body, --text-label, --text-caption, --text-title, --text-page-title)
- Add --text-md (15px) escape hatch and --tracking-wider (0.05em) token
- Add prefers-reduced-motion fallbacks locking fluid headings to fixed midpoints
- Document scale ratio (1.25x major third, 14px base) with reference table
- Migrate h1/h2 in app-shell.css to semantic aliases
- Migrate 4 raw letter-spacing values to --tracking-* tokens
- Extend TypographyTokenContractTest with 3 new tests (letter-spacing, aliases, reduced-motion)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
--app-font-size was still 100% (16px), disconnected from the type scale
tokens. Now references var(--text-base) so the 14px enterprise baseline
actually takes effect. Remove empty responsive @media font-size placeholders
in variables.base.css and raw heading size overrides in app-shell.css that
fought the clamp()-based tokens.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shift all typography tokens down to match industry standard for admin
dashboards (Atlassian, Carbon, Primer, SAP Fiori all use 14px base).
Browser root stays at 16px for accessibility zoom compliance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace mislabeled sidebar title 10px with var(--text-2xs) (F-001)
- Replace font-size: small with var(--text-sm) in app-shell (F-002)
- Annotate @media heading overrides as potentially redundant with clamp() (F-003)
- Correct type scale comment to "approximate major-third ratio" (F-004)
- Differentiate h5 (--text-sm) from h4 (--text-base) (F-005)
- Tighten contract test to flag bare normal/bold keywords (F-006)
Task run: UI-TYPOGRAPHY-SCALE-002
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Define a major-third (1.25) type scale as CSS custom properties in the tokens
layer (--text-2xs through --text-5xl), along with line-height (--leading-*),
font-weight (--font-*), and letter-spacing (--tracking-*) tokens. Migrate all
28 core CSS files from ad-hoc values to token references with clamp()-based
responsive scaling for headings. Add TypographyTokenContractTest to enforce
token-only usage via grep-based assertions.
Task run: UI-TYPOGRAPHY-SCALE-001
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New page at /admin/system-info with three tabs:
- Overview: PHP version, SAPI, environment, 6 health checks (DB, schema,
storage, RBAC baseline, admin role, scheduler heartbeat)
- Modules: table of active modules with version, dependencies, permission count
- Permissions: active/inactive counts and per-source breakdown
Gated behind new system_info.view permission assigned to Admin role.
No mutations — purely diagnostic/observability.
Includes SystemHealthService, SystemInfoService, SystemHealthRepository
with interface, DI registration, i18n keys (de+en), idempotent DB update
script, and 17 new PHPUnit tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New contracts enforce repository layer isolation, no HTML in services,
no DB calls in views, module session key prefixes, and asset versioning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add bin/ scripts to PHPStan scanFiles, suppress property.onlyWritten for
by-ref test properties, add exhaustive default match arm, use explicit
expects() on mock stubs, fix data provider return type, and simplify
redundant null check in NotificationServiceTest.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Values are already guaranteed by the repository layer, so defensive
guards like ?? 0, ?? '' and is_array() are unnecessary.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set defaultTestSuite="CoreCore" to prevent 222 duplicate file warnings,
and replace deprecated isType() with isArray()/isString() in notification
listener tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract ModuleManifestLoader for standalone manifest loading (used by
module-deactivate and ValidateCommand)
- Extract SqlStatementParser, ModuleMigrationRepository, and
ModuleMigrationService from bin/module-migrate.php to enforce strict
layering (SQL in repository, orchestration in service)
- Delete bin/scheduler-run.php (fully superseded by bin/console scheduler:run)
- Update docs and test fixtures to reference bin/console
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass AppContainer to SessionProvider::clear() so all module providers use
SessionStoreInterface consistently instead of raw $_SESSION. Also add
loading and error states to the notification bell dropdown (GR-UI-014).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In-app notification system with topbar bell icon, dropdown panel,
mark-as-read/dismiss actions, and scheduled cleanup of old read
notifications. Includes event listeners for user.created and
user.deleted events, authorization policy, and full test coverage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each module can now ship its own i18n/ directory with translation files
(default_de.json, default_en.json). At boot time, ModuleI18nLoader
preloads core translations then merges module translations on top via
Reflection into I18n::$strings — the t() helper stays unchanged.
- Add i18nPath property to ModuleManifest
- Add ModuleI18nLoader with Reflection-based preload
- Extract 59 module-exclusive keys from core i18n into module files
(notifications: 8, bookmarks: 41, addressbook: 10)
- Expand TranslationKeysTest to scan modules/ and validate against
merged core + module translations
- Add ModuleI18nContractTest for per-module de/en parity and completeness
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
make:module <id> generates the full module directory structure with
manifest (all 18 fields), ContainerRegistrar stub, AuthorizationPolicy
stub, and empty pages/templates/web/tests directories. Prevents
duplicate IDs and invalid formats.
module:validate <id> (or --all) checks manifest parsing, class existence,
interface compliance, namespace rules, permission key format, template
file existence, and dependency resolution — catching errors that would
otherwise only surface at boot time. Temporarily registers module PSR-4
paths so non-enabled modules can be validated too.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- tools/codex-skills/ removed — skills now live in .agents/skills/
- docs/reference-agents-*.md removed — replaced by .agents/workflow.md
- FilterDrawerContractTest split into Runtime + Template contract tests
- bin scripts and .gitignore updated for .agents/ paths
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs in the toast redesign:
1. Variant backgrounds (success/warning/info/error) used rgba with 12-20%
opacity, making toasts semi-transparent over page content. Fixed by
layering the tint over a solid background via linear-gradient compositing.
2. Flash messages with scoped paths including query strings (e.g.
edit pages with ?return=...) were not matched by Flash::peek(Request::path())
since path() strips the query. Added fallback to pathWithQuery() matching.
Also moved flash partial before JS init scripts to ensure DOM is present
when the component runtime mounts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
deactivateOrphaned() treated ALL is_system=1 permissions as module-owned.
Core seed permissions (users.view, tenants.view, etc.) are also is_system=1,
so they were incorrectly deactivated — breaking admin panel access.
Fix: scan all module manifests on disk (enabled or not) to build the set of
module-owned permission keys. Only deactivate permissions whose key appears
in that set. Core seed permissions are never touched.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New docs/reference-cli-commands.md covers all commands, bootstrap helpers,
and how to add new commands. Updated lokale-entwicklung, entwickler-checkliste,
betriebscheck-doctor, index, and web/index.php error message to reference
bin/console instead of legacy bin/*.php scripts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ConsoleApplication.discoverCommands() scans lib/Console/Commands/ automatically;
adding a new command no longer requires editing bin/console
- Command base class provides bootstrapApp(), bootstrapModuleApp(), requireBinScript()
and projectRoot() helpers — eliminates fragile 4-level relative require_once paths
- Per-command --help support via optional usage() method on Command
- Docker-compose scheduler updated to use bin/console scheduler:run
- Old bin scripts now emit deprecation hint to stderr when called directly
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>