major
#29400
Introduce a first-class application "operation mode" service (OperationMode enum + ApplicationModeService)
Motivation
There is no first-class notion of "what operational state is this application in" in the engine. The prod/dev distinction is scattered across Environment.isDeployed() (~22 callers) plus independent is-deployed flags in ThemeFactory and FileCompiler, and assorted tl_* system properties; runtime maintenance state lives in MaintenanceWindowManager. Downstream modules that need to ask "what mode are we in?" have no single API and must reconstruct the answer.
Concretely, tl-ai already does exactly this reconstruction, hardcoded in TopLogicSecurityContext.executionMode(), to decide whether an agent tool may run (e.g. STRUCTURAL tools are denied in production). We want a single, engine-owned source of truth these consumers can query.
Scope of this ticket
The **engine service only**. tl-ai is not modified here; the consumer rewire and any modernization of MaintenanceWindowManager are separate follow-up tickets.
Design (decided)
- New strict enum OperationMode implements ExternallyNamed in a new package com.top_logic.base.operation (module com.top_logic / tl-core), exactly three members: DEVELOPMENT("development"), TEST("test"), PRODUCTION("production").
- New ApplicationModeService extends ConfiguredManagedClass<Config> in the same package, modelled on TimeRangeService. Public API: getInstance(), OperationMode getMode(), boolean isMaintenanceActive().
- **Maintenance is a separate axis, folded in** - NOT a member of OperationMode (environment and runtime maintenance are orthogonal; a production install can be in a maintenance window). isMaintenanceActive() delegates (null-safe) to MaintenanceWindowManager; javadoc documents the intended fold for consumers.
- **Default derivation when mode is unconfigured** (backward compatible): isTesting() -> TEST; else !Environment.isDeployed() -> DEVELOPMENT; else PRODUCTION. An explicit configured mode always wins.
- **Per-deployment override with no code change**: config mode="%OPERATION_MODE%", alias %OPERATION_MODE% = ${env:tl_operation_mode:} in top-logic.xml; empty falls through to the derivation. Module enabled + configured in top-logic.config.xml (mirror the TimeRangeService entries).
- The environment axis is **boot-time / static** (must not flip under a running app). Maintenance stays runtime-switchable via the existing manager, untouched.
Consumer contract (follow-up)
This service becomes the single source of truth that tl-ai's TopLogicSecurityContext.executionMode() will consume, replacing its hardcoded MaintenanceWindowManager + Environment.isDeployed() reads. tl-ai currently builds against engine 7.11.0, so a backport may be needed when the consumer side lands.
Open questions / possible follow-ups
- Whether to add STAGING later (no existing analogue today).
- Modernize MaintenanceWindowManager's raw int state constants to an enum.
- Route the scattered Environment.isDeployed() callers and the ThemeFactory/`FileCompiler` is-deployed flags through this service.
Migration
The service was implemented as com.top_logic.base.operation.OperationModeService (getInstance(), getMode()), and the derivation was turned around: the operation mode is the source of truth, and Environment.isDeployed() is derived from it.
Environment.isDeployed() no longer detects a developer workspace from the class-path layout (isJarFile() combined with tl_developerMode). It returns true unless the system property or environment variable tl_operation_mode is set to development. tl_developerMode and Environment.isJarFile() are deprecated and have no effect any more.
Consequence: an IDE or Maven run of an application that does not set the operation mode is treated as a production deployment (deployed theme and script handling, no modular resource path, IDEOnly commands hidden). Production deployments need no change.
- Replace -Dtl_developerMode=true by -Dtl_operation_mode=development in every Eclipse .launch, .mvn/jvm.config, start script and CI job that runs the application from the workspace. Values: development, test, production (the default; test is derived under the test container). The archetype launch configurations and the engine's own bin/launch/*.launch files were changed accordingly.
- Optionally set tl_operation_mode=production explicitly in deployment descriptors (the archetype's Dockerfile_template does) for clarity.
- The Chainsaw socket appender of the default logging configuration is enabled by -Dtl_logReceiver=true instead of developer mode (see #29383).
- Application code that decided on Environment.isDeployed() should ask OperationModeService.getInstance().getMode() (OperationMode.DEVELOPMENT, TEST, PRODUCTION); the executability rule IDEOnly already does.