TopLogic - the automated application engine
  • Releases
  • Dokumentation
  • Github
  • Discord
  1. Home
  2. Releases
  3. TL_8.0.0-alpha13
  4. Migration guide

8.0.0-alpha13
TopLogic Release

2026-11-13

Markdown version, e.g. for an LLM updating an application.

Migrating a TopLogic application from 8.0.0-alpha12 to 8.0.0-alpha13

Audience: an AI coding agent (or developer) upgrading a TopLogic application project. Sources: release notes https://dev.top-logic.com/tl/content/releases/TL_8.0.0-alpha13/ and the TopLogic Trac tickets flagged RequiresMigration for milestone TL_8.0.0-alpha13 (ticket numbers are given as #NNNNN; the per-ticket page is https://dev.top-logic.com/tl/content/releases/TL_8.0.0-alpha13/NNNNN/).

How to use this document

  1. Work through the sections in order. Section 1 must be done first (the project does not build otherwise); the rest can be done in any order.
  2. Every step has a Detect line: run the search in the application's source tree. If it finds nothing, the step does not apply — skip it. Do not invent changes for steps that do not apply.
  3. Many changes are reported as configuration errors at application start or when a view is loaded (not at compile time). After migrating, start the application, log in, and open every view; read the log for ConfigurationException / configuration errors.
  4. The engine ships developer articles in its jars (META-INF/tl-docs/**/*.md). If the tl-mcp tools are available, read them with read_doc (names given below as doc:<name>).
  5. Ticket descriptions on Trac are partly German; this document translates the instructions.
  6. Some steps delete data (#30079 comments) or change access rights (#30255). Report them to the user before starting the migrated application against a production database.

Search commands below assume the project root as working directory and GNU grep.


1. Build and platform

1.1 Version bump

Set the TopLogic version to 8.0.0-alpha13 (parent tl-parent-app / tl-parent-core version, or the tl.version property).

1.2 Classic UI parts moved into separate modules (#29961) — mandatory for classic applications declaring modules individually

React applications no longer contain modules of the classic UI. Applications using tl-starter-classic, tl-layout-formeditor or tl-chat-classic need no change; tl-addons-loginmessages brings tl-addons-loginmessages-core itself.

A classic application that declares its modules individually and uses none of these adds, depending on the functions it uses:

Module to add Provides
tl-model-search-classic model-based search, script console, Ace editor for TL-Script, code and JSON attributes and @Code configuration properties. Without it these attributes are edited without code editor, silently.
tl-model-wysiwyg CKEditor for HTML attributes (before possibly obtained via tl-starter-react, tl-service-openapi-common or tl-bpe)
tl-ai-classic classic AI administration (before part of tl-ai-core)
tl-monitoring classic monitoring UI (before obtained via tl-layout-view)
tl-xio-classic command com.top_logic.xio.command.XMLImportCommand (before part of tl-xio); class name and configuration unchanged

Own code using classes of the classic parts (e.g. SearchSelectorComponent, TLScriptCodeEditorControl, StructuredTextControl, StructuredTextConfigService) needs the respective classic module as dependency; package and class names are unchanged.

Dropped transitive dependencies: tl-model-search no longer brings tl-reporting-flex (JFreeChart reports, classes under com.top_logic.reporting.flex) and tl-layout-codeedit; tl-ai-core no longer brings tl-chart-chartjs (Chart.js diagrams of the classic UI). An application that got these modules only that way declares them itself or adds tl-model-search-classic resp. tl-ai-classic.

Applications with classic scripted tests (*.script.xml) not generated by the archetype declare the test jar themselves:

<dependency>
	<groupId>com.top-logic</groupId>
	<artifactId>tl-layout-scripting-template</artifactId>
	<type>test-jar</type>
	<scope>test</scope>
</dependency>

Detect:

grep -n -e '<artifactId>tl-' pom.xml */pom.xml 2>/dev/null
grep -rln -e 'SearchSelectorComponent' -e 'TLScriptCodeEditorControl' -e 'StructuredTextControl' \
  -e 'StructuredTextConfigService' -e 'XMLImportCommand' -e 'com.top_logic.reporting.flex' \
  -e 'chartjs' --include=*.java --include=*.xml src
find . -name '*.script.xml' -not -path '*/target/*' | head

(Compare the declared modules with the table; a classic application without tl-starter-classic must check every row.)

1.3 Classic BPE task processing in tl-bpe-classic (#30065)

Classic applications that include tl-bpe directly (not via tl-bpe-modeler) and use the classic task processing (layout templates tl-bpe/startProcess*.template.xml, tl-bpe/advanceProcess*.template.xml, classes under com.top_logic.bpe.layout) or the PDF export add:

<dependency>
  <groupId>com.top-logic</groupId>
  <artifactId>tl-bpe-classic</artifactId>
</dependency>

Applications using tl-bpe-modeler or tl-bpe-app get tl-bpe-classic transitively. A React application includes tl-bpe-view and shows the task page (bpe/tasks.view.xml) where it wants it.

Further changes affecting own dependencies and code:

  • tl-bpe no longer brings tl-layout-formeditor. An application using classes or layouts of the form editor that got the module only via tl-bpe declares tl-layout-formeditor itself (or gets it via tl-bpe-classic).
  • com.top_logic.bpe.modeler.copy.DeepCopy is now com.top_logic.bpe.util.DeepCopy (module tl-bpe); adapt imports.
  • BPMLUpdateCommand.updateCollaboration(Collaboration, Source, boolean) and BPMLUpdateCommand.updateCollaborationInTransaction(...) are removed. Use BPMLDefinitions.updateCollaboration(...) (with Source or BinaryContent); the caller opens the transaction itself.

Detect: grep -rn -e '<artifactId>tl-bpe' -e 'startProcess' -e 'advanceProcess' -e 'com.top_logic.bpe.layout' -e 'formeditor' -e 'DeepCopy' -e 'BPMLUpdateCommand' --include=*.xml --include=*.java .

1.4 AI browser tools in the optional module tl-ai-browser (#30253)

  • An application that uses the browser tools (web-browser.*) or cookie pools adds the dependency com.top-logic:tl-ai-browser. An existing database is taken over on startup without further steps.
  • An own agent that browses and names the skill TL_webAccess additionally names the skill TL_webBrowser.
  • The classes moved to the package com.top_logic.tl.ai.browser (BrowserToolsService, BrowserTool, …) and com.top_logic.tl.ai.browser.cookiepool (CookiePoolService); configuration and code referencing them by class name must be adapted, e.g. <config service-class="com.top_logic.tl.ai.browser.BrowserToolsService">.
  • An application without tl-ai-browser whose database already contains cookie pools keeps the table CookiePool unused; doc:ai/browser describes the migration that removes it.
  • An exclusion of driver-bundle from tl-ai-core (workaround) can be removed.

Detect: grep -rn -e 'web-browser' -e 'TL_webAccess' -e 'BrowserToolsService' -e 'BrowserTool' -e 'CookiePool' -e 'driver-bundle' .


2. Server configuration and database

2.1 Scheduler: one active/inactive switch per task (#30057) — configuration

  • The task configuration property blocked-by-default is removed. Replace blocked-by-default="true" with enabled="false".
  • The task configuration property blocking-allowed is removed without replacement; every task can be activated and deactivated by an administrator.
  • Blocking states stored in the database are taken over automatically (blocked = inactive, unblocked = active).

Java API and classic layouts: see 5.6.

Detect: grep -rn -e 'blocked-by-default' -e 'blocking-allowed' .

2.2 External login: user-mapping / user-name-extractor only in element form (#30172)

The configuration properties user-name-extractor and user-mapping (pac4j clients in Pac4jConfigFactory, J2eeRemoteUserAuthenticationServlet) no longer accept a bare class name as attribute value. Replace

<keycloak name="..." user-mapping="com.example.MyMapping">

by

<keycloak name="...">
  <user-mapping class="com.example.MyMapping"/>
</keycloak>

The element form <user-mapping class="..." .../> with further attributes stays unchanged. Java code reading getUserMapping() / getUserNameExtractor() from the client configuration obtains the instances from Pac4jConfigFactory.getUserMapping(String) / getUserNameExtractor(String) instead.

Detect: grep -rn -e 'user-mapping="' -e 'user-name-extractor="' -e 'getUserMapping()' -e 'getUserNameExtractor()' .

2.3 Contact configuration (#30079)

The file WEB-INF/conf/contactConf.xml is gone; metaConf.txt of tl-contact includes contactConf.config.xml directly. An application that overrides or references contactConf.xml in its own metaConf.txt must adapt this.

Detect: grep -rn 'contactConf.xml' .

2.4 AI model catalog: Claude 5.5 (#30251)

  • An application relying on the shipped model lists (no tl_anthropic_models / tl_xai_models) now runs its agents on claude-sonnet-5-5 (default) resp. offers grok-4.7,grok-4.3. To keep the previous models, set the variables (tl_anthropic_models=claude-sonnet-4-6,claude-opus-4-7,claude-haiku-4-5, tl_xai_models=grok-4.3,grok-4-1-fast). Note: grok-4-1-fast is no longer in the bundled catalog (next bullet) — declare it in <model-overrides> if you keep it.
  • An agent tool access policy (AiToolAccessService) may grant only models a configured provider offers; otherwise the policy is refused and the service does not start. A policy granting a model no longer in the shipped lists (e.g. <grant models="claude-haiku-4-5"/>) changes the grant to a current model (claude-haiku-5-5) or sets tl_anthropic_models / tl_xai_models to include it.
  • A model the bundled catalog no longer knows (retired models such as claude-sonnet-4-20250514, claude-opus-4-1, grok-4-1-fast) gets no context window, output limit and prices. Switch to a current model or declare it in the <model-overrides> of LLMProviderService.
  • Java code constructing ModelInfo.ModelCost / ModelInfo.TieredCost passes a list of tiers resp. the tier's threshold; CostCalculator.TIER_THRESHOLD is gone (ModelCost.tierFor(promptTokens) answers the applicable tier).

Detect:

grep -rn -e 'tl_anthropic_models' -e 'tl_xai_models' -e 'models=' -e 'model-overrides' -e 'claude-' -e 'grok-' \
  -e 'ModelCost' -e 'TieredCost' -e 'TIER_THRESHOLD' .

2.5 Configuration variables are overridden from the environment (#30268)

A configuration variable that the application overrides with a fixed value without ${env:…} expression (e.g. <alias name="%STORAGE_PATH%" value="./data"/>) is now still replaced by tl_<name> from JNDI, system property or environment (here tl_storage_path). If the fixed value must win even when the environment sets tl_<name> (e.g. a Docker image setting tl_storage_path), remove the variable from the installation's environment or set the wanted value there.

Detect: grep -rn '<alias name="%' --include=*.xml . (then check the deployment environment for tl_<name>)

2.6 Self-service (#30041, #30183, #30223) — configuration and texts

  • The login hook class com.top_logic.security.selfservice.invitation.CreateLoginHoook is renamed to CreateLoginHook. An application referencing it (e.g. in <login-hooks> of MainLayout$GlobalConfig) or in code must use the new name.
  • An application that overrides the keys tl.security.selfservice.mail.* in its own bundle moves the texts to the options invitation-text, verification-text, reset-password-text, reset-mfa-text of InvitationModule. The bundle tl-security-selfservice-core.app-messages no longer exists; a configuration referencing it drops the reference.
  • An application that redefines a mail script may use the configured texts, passed as fourth and fifth argument (subject, body), unfilled.
  • An application with tl-security-selfservice-core starts the CaptchaService. If a captcha is used in a cluster, configure a shared key (<hmac-key name="tl_captcha_hmac_key"/>) and serve the application via HTTPS.
  • Resource keys removed from com.top_logic.security.selfservice.invitation.I18NConstants: ERROR_NO_INVITATION__ID, UPDATED_INVITATION_CODE, DELETED_INVITATION, ERROR_CODE_MISMATCH, ERROR_CODE_MISMATCH_TOO_MANY_TIMES, REQUEST_CODE_NOT_ALLOWED__TIMEOUT. An application overriding them drops them.
  • Classic layouts changed in the layout editor: an application that stored its own copy of admin/security/accounts/existingInvitations/* or admin/security/accounts/inviteUsers/dialog.layout.xml re-applies the new columns and commands (incl. the field / column language) to it.
  • Automatic: existing invitations get an expiry date (update time plus invitation-validity) and no language (sent in the application's default language); schema (attribute locked, type Registration) and the form definition of the user administration are migrated at startup.

Java API: see 5.12.

Detect:

grep -rn -e 'CreateLoginHoook' -e 'tl.security.selfservice.mail' -e 'selfservice-core.app-messages' \
  -e 'ERROR_NO_INVITATION' -e 'UPDATED_INVITATION_CODE' -e 'DELETED_INVITATION' -e 'ERROR_CODE_MISMATCH' \
  -e 'REQUEST_CODE_NOT_ALLOWED' -e 'CaptchaService' -e 'tl-security-selfservice' .
find . -path '*layouts/admin/security/accounts*' -not -path '*/target/*'

2.7 Unique link tables and bag tables (#30031) — check own link tables

The migration Ticket_30031_unique_link_tables (tl-element) runs automatically: it creates the bag tables, moves the links of all bag references there, removes duplicate links (same source, metaAttribute, dest in overlapping revisions) over the whole history from the standard tables (incl. application tables derived from them, each cleaned link is logged) and replaces the source index by a unique index.

Applications with own link tables derived from hasWrapperAttValueBaseAssociation inherit the unique index. If they store bag references (explicitly via <storage table="..."/>), those links are not cleaned; the migration reports them as errors and creating the index fails on duplicates. Such references must be reconfigured to a bag table (derived from hasWrapperAttValueBagBaseAssociation) and their links moved with <move-links> in an own migration.

Java API: ListStorage.listConfig(...), SetStorage.setConfig(...) and ImageGalleryStorage.imageGalleryConfig(...) have an additional parameter boolean bag (previous behaviour: false; true for a bag reference).

Detect:

grep -rn -e 'hasWrapperAttValueBaseAssociation' -e '<storage table=' --include=*.xml .
grep -rn -e 'listConfig(' -e 'setConfig(' -e 'imageGalleryConfig(' --include=*.java .

2.8 Own migration processors: SQLFactory and DBHelper (#30163, #30167)

  • SQLFactory.modifyColumnName(SQLTable, String, DBType, String), modifyColumnType(SQLTable, String, DBType) and modifyColumnMandatory(SQLTable, String, DBType, boolean) are removed. Callers pass the full definition of the column after the change:
    modifyColumnName(table, columnName, newName, type, mandatory, binary, size, precision)
    modifyColumnType(table, columnName, type, mandatory, binary, size, precision)
    modifyColumnMandatory(table, columnName, type, mandatory, binary, size, precision)
    
    or the variants with a DBColumn (modifyColumnName(table, column, newName), modifyColumnType(table, column), modifyColumnMandatory(table, column)). Values formerly set via setters on SQLModifyColumn (setSize(..), setBinary(..), setPrecision(..), setMandatory(..)) go into these parameters:
    // before
    modifyColumnType(table("T"), "C", DBType.STRING).setSize(150).setBinary(true)
    // after
    modifyColumnType(table("T"), "C", DBType.STRING, mandatory, true, 150, 0)
    
    mandatory is the mandatory flag the column shall have after the change (for a pure type change its current one).
  • SQLFactory.addColumn(..) has no parameter defaultValue any more; SQLAddColumn.getDefaultValue() / setDefaultValue(..) are gone. A processor adding a mandatory column with default creates the column non-mandatory, fills it with UPDATE, then sets it mandatory with SQLFactory.modifyColumnMandatory(..).
  • Own SQL dialects (subclasses of DBHelper): the last parameter Object defaultValue of appendChangeColumnName(..), appendChangeColumnType(..) and appendChangeMandatory(..) is gone; appendDefaultValue(..) and appendDBType(Appendable, DBType, String, long, int, boolean, boolean, Object) no longer exist (use the variant without default value).
  • AddDatabaseColumnProcessor: default-value no longer creates a database default, it only fills existing rows. A permanent database default needs an own sql step.

Detect:

grep -rn -e 'modifyColumnName' -e 'modifyColumnType' -e 'modifyColumnMandatory' -e 'addColumn(' \
  -e 'DefaultValue' -e 'extends .*DBHelper' -e 'appendChangeColumn' -e 'appendChangeMandatory' -e 'appendDBType' \
  --include=*.java .
grep -rn -e 'default-value' --include=*.xml src/main/webapp/WEB-INF/conf 2>/dev/null

3. Security / access checks

3.1 Group mapper of the ElementAccessManager removed (#30096) — mandatory if configured

  • Remove the entry <group-mapper .../> from the configuration of the AccessManager.
  • Every role rule of type reference whose path ends at a Contacts:PersonContact gets an additional step to the person:
    <path>
    	<step attribute="..."/>
    	<step attribute="tl.accounts:Person#contact" inverse="true"/>
    </path>
    
  • Own Mapping implementations with more logic than "group or person to group" must be expressed as navigation steps in the affected role rules. If the mapping is not yet a reference in the model, model it there first.
  • The classes com.top_logic.element.boundsec.manager.rule.GroupMapper, com.top_logic.tool.boundsec.manager.SimpleGroupMapper and com.top_logic.contact.boundsec.manager.rule.ContactGroupMapper are removed; remove own code using or extending them.

Detect: grep -rn -e 'group-mapper' -e 'PersonContact' -e 'Mapping' -e 'GroupMapper' --include=*.xml --include=*.java .

3.2 Comments module and mandator-aware contacts removed (#30079) — deletes data

Comments: the migration tl-element:Ticket_30079_Remove_comments deletes the module tl.comments and the table Comment. All existing comments are deleted. An application that declares attributes of type tl.comments:Comment in its model must

  1. remove these attributes from its model (*.model.xml) and from layouts, forms and the security configuration (grants on the module tl.comments),
  2. add an own migration that depends on tl-element:Ticket_30079_Remove_comments and deletes the attributes in its pre-processors, e.g.:
    <dependencies>
    	<dependency name="Ticket_30079_Remove_comments" module="tl-element"/>
    	...
    </dependencies>
    <pre-processors>
    	<!-- Reference: also deletes the stored links. -->
    	<delete-reference name="my.module:MyType#comments"/>
    	<!-- Derived property. -->
    	<delete-property name="my.module:OtherType#comments"/>
    </pre-processors>
    
  3. remove usages of the template commons/comments/comments.xml and of classes from com.top_logic.element.comment.

Mandator-aware contacts: an application using classes from com.top_logic.contact.mandatoraware or com.top_logic.contact.search (e.g. COSPersonContact / COSCompanyContact as implementation of contact types, the SAP/Excel importers or ContactSearchComponent) must remove them or replace them by own implementations. Usages of the filter SupportsContacts or the generator CompanyContact of FilterFactory / GeneratorFactory must be removed. Likewise com.top_logic.contact.filter.ContactListModelBuilder and com.top_logic.contact.external.importer.ExcelExternalContactImporter are removed; remove their usages in layouts, configuration and code.

Detect:

grep -rn -e 'tl.comments' -e 'commons/comments' -e 'com.top_logic.element.comment' \
  -e 'contact.mandatoraware' -e 'contact.search' -e 'COSPersonContact' -e 'COSCompanyContact' \
  -e 'ContactSearchComponent' -e 'SupportsContacts' -e 'CompanyContact' \
  -e 'ContactListModelBuilder' -e 'ExcelExternalContactImporter' .

3.3 Attribute WRITE grants checked on objects created in the same transaction (#30255)

An application script that runs with security (the default) and sets an attribute with an attribute-level WRITE grant on an object created in the same transaction — e.g. a "created by" field the user may not edit — is now refused for a user without that grant. Either grant the role the right on the attribute or run that script without security.

Workarounds for the former wrong refusal of the pre-commit guard (e.g. a SecurityContextFactory answering canCreate with yes for a container created in the same turn) can be removed.

The classes com.top_logic.tl.ai.core.security.PreCommitGuard and ModelSecurityPreCommitGuard are removed. Remove own code using or implementing them; the tools check the rights of a write themselves (Java tools via ModelSecurity, TL-Script via the engine).

Detect: grep -rn -e '<grant' --include=*.xml . | grep -i attribute and check scripts that create objects and set such attributes (creation commands, <execute-script>, AI tools); grep -rn 'PreCommitGuard' --include=*.java --include=*.xml .


4. React views (*.view.xml)

4.1 Sidebar of the shell in sections (#30044) — mandatory for shell overlays

The shell structures its sidebar into <section id="application"/> (empty, filled by the application) and <section id="system"> with the administration. An overlay of the shell (WEB-INF/views/app.view.xml of an application) that still names config:reference="system-separator" or uses <separator> or <header-item> can no longer be loaded (the application answers /view/ with HTTP 500). Adapt:

  • Entries placed before the system section with config:position="before" config:reference="system-separator" are written into the application section without these two attributes:
    <sidebar config:ref="sidebar">
      <items>
        <section id="application">
          <nav-item id="home">...</nav-item>
        </section>
      </items>
    </sidebar>
    
  • A <separator/> between own entries: put the entries before and after it into one <section id="…"> each.
  • A <header-item id="x"> with label: replace it by <section id="x"> with the same label (optional label, icon) and move the entries it covered into the section. Entry ids stay unchanged.

Detect: grep -rn -e 'system-separator' -e '<separator' -e '<header-item' --include=*.view.xml .

4.2 Colour role of rows, cells and displays (#30256)

A script in row-class, cell-class or css-class (on ResourceDisplayByExpression) that yields one of the classes tl-status-error, tl-status-warning, tl-status-success or tl-status-info moves to the corresponding role property and yields the role name without prefix:

<table row-class="$row.get(`my:Part#amount`) > 100 ? 'tl-status-warning' : null"/>

becomes

<table row-role="$row.get(`my:Part#amount`) > 100 ? 'warning' : null"/>

Likewise cell-class → cell-role on the column and css-class → role on ResourceDisplayByExpression. A script that yields further own classes besides the status class keeps those in the class property. A display with a role appears as a pill, not as coloured text.

The TL-Script functions securityGrantsRowClass and securityGrantsCellClass are renamed to securityGrantsRowRole and securityGrantsCellRole, yield a role name and belong into row-role resp. cell-role.

Java: RowClassFunction is renamed to RowFunction (RowFunction.rowClass(...), RowFunction.rowRole(...)); the record ColumnSetup has the additional component cellRole after cellClass.

Detect: grep -rn -e 'tl-status-' -e 'securityGrantsRowClass' -e 'securityGrantsCellClass' -e 'RowClassFunction' -e 'ColumnSetup(' .

4.3 Showing a unit only for one type: <visibility> instead of <access-control type> (#30258)

An <access-control> whose type only serves to hide a unit for objects of other types is replaced by

<visibility inputs="..." expr="$x.instanceOf(`<Type>`)"/>

(or complemented by it, if an access check is wanted too; its type is then a type all checked objects have).

Detect: grep -rn '<access-control' --include=*.view.xml . | grep 'type='

4.4 <job-status> closable by default (#30250)

<job-status> offers a close button for an ended job by default, which clears the input channel. Where the display of an ended job must stay, set closable="false".

A cancelled job whose body returns a value regularly after the cancellation delivers this value as jobResult(...).

Detect: grep -rn -e '<job-status' -e 'jobResult' --include=*.view.xml .

4.5 Self-service views renamed (#30183)

React (tl-layout-view-selfservice, affects preview versions with #30041): the view invitation-code.view.xml is now verification-code.view.xml, the action <check-invitation-code/> is <check-verification-code/>, <accept-invitation/> is <create-own-account/>. An application that overrides these views or uses the actions in own views renames them.

Detect: grep -rn -e 'invitation-code' -e 'check-invitation-code' -e 'accept-invitation' .

4.6 Agents and tools at /agent-api (#30200)

The key commands are gone. Objects are addressed with the IDs from the observation (key / nodeId / rowId / value, see the respective argument schema); the table takes rowId instead of rowIndex, drop expects keys as JSON array. Argument schemas are under schemas and referenced via $ref; there is no params field any more. The compact view is called mode=actions, pretty=true gives indented output. Adapt own clients, agent prompts and recorded scripts.

Detect: grep -rn -e 'agent-api' -e 'rowIndex' -e 'ByKey' -e '"params"' .

4.7 Log view of the administration moved (#30059)

The view admin/system/logs.view.xml (module tl-layout-view) is now admin/monitoring/logs.view.xml. A same-path overlay of the application (WEB-INF/views/admin/system/logs.view.xml) or a <view-ref view="admin/system/logs.view.xml"/> must use the new path; an overlay of the old path has no base any more.

Detect: grep -rn 'admin/system/logs' . ; find . -path '*WEB-INF/views/admin/system/logs.view.xml' -not -path '*/target/*'


5. Java APIs and TL-Script

5.1 View commands without HandlerResult (#30151)

Own ViewCommand implementations, CommandModels, ButtonActions or @ReactCommandHandler methods:

  • return type void instead of HandlerResult; return HandlerResult.DEFAULT_RESULT; is dropped;
  • return HandlerResult.error(key) becomes throw new TopLogicException(key); return HandlerResult.notExecutable(...) resp. checking an ExecutableState becomes throw CommandErrors.notExecutable(state);
  • a caller evaluating command.execute(...) catches TopLogicException instead of checking isSuccess();
  • a handler answering its caller declares the answer as msgbuf message and returns it instead of ReactCommandReply.create().put(...).toResult(). Client code reads the answer fields as before; a failed command now answers with success: false and error.

Detect: grep -rn -e 'ViewCommand' -e 'CommandModel' -e 'ButtonAction' -e 'ReactCommandHandler' -e 'ReactCommandReply' --include=*.java . | grep -v '^.*import'

5.2 Command lists and ViewCommandModel (#30063, #30207)

  • Code reading or setting the command list of a configuration of CommandCarrierElement or FormElement (getCommands()) gets List<ViewCommand.Config> instead of List<PolymorphicConfiguration<? extends ViewCommand>>. .view.xml files are unchanged.
  • ViewCommandModel.execute(ReactContext, Object) is now execute(Object); the command runs in the context the model was created with.
  • The public constructors of ViewCommandModel and ViewUploadCommandModel expect the ViewContext as first argument. ViewCommandModel.create(...) / forCommand(...) no longer accept a null context.
  • RowCommandColumn.RowCommand.of(...) expects no context any more.
  • An own wrapper around a ViewCommandModel that only sets another execution context is superfluous.

Detect: grep -rn -e 'CommandCarrierElement' -e 'FormElement' -e 'ViewCommandModel' -e 'ViewUploadCommandModel' -e 'RowCommand.of' --include=*.java .

5.3 Own React controls: stable object IDs for scripting (#30200)

  • Subclasses of ReactControl overriding scriptingScalarState() override scriptingScalarState(ProjectionMode mode) instead (the variant without parameter is final). Implementations of ScriptingNode.scriptingState() become scriptingState(ProjectionMode mode). An object is entered into the projection with ScriptingModelKey.putIdentity(projection, mode, objectIdentities(), ...) instead of by its key.
  • A control whose commands address objects holds their IDs in an ObjectIdIndex, returns it from objectIdentities() and marks the ID arguments of its typed argument interfaces with @DisplayedObjectId (lists additionally with @JsonBinding(SimpleJsonListBinding.class)). A recordCommand override that only translates IDs into keys is dropped.
  • Removed types: ModelKeyArguments (constant KEY is now in ScriptingModelKey), SelectByKeysArguments, SelectByNameArguments, DropObjectsArguments and the *ByKeyArguments of table and tree.

Detect: grep -rn -e 'scriptingScalarState' -e 'scriptingState(' -e 'ModelKeyArguments' -e 'SelectByKeysArguments' -e 'SelectByNameArguments' -e 'DropObjectsArguments' -e 'ByKeyArguments' -e 'recordCommand' --include=*.java .

5.4 Model value listeners (#30132)

An own class implementing com.top_logic.layout.form.ValueListener that is configured in a model annotation <value-listeners> must implement com.top_logic.model.annotate.AttributeValueListener instead: valueChanged(TLFormObjectBase object, TLStructuredTypePart attribute, Object oldValue, Object newValue) receives the edited object (form overlay) and the attribute instead of the FormField. Model files and <script> listeners are unchanged.

Detect: grep -rn -e 'implements .*ValueListener' -e 'value-listeners' .

5.5 DynamicVisibility moved (#30205)

DynamicVisibility is now in package com.top_logic.model.annotate (module tl-core) instead of com.top_logic.layout.view.form. Adapt imports:

import com.top_logic.model.annotate.DynamicVisibility;

Detect: grep -rn 'layout.view.form.DynamicVisibility' --include=*.java .

5.6 Scheduler API and classic scheduler layouts (#30057)

  • Scheduler.enable(Task) / disable(Task) / isTaskEnabled(Task) / isEnabled(Task) and blockTask(Task) / unblockTask(Task) / isTaskBlocked(Task) are replaced by activate(Task) / deactivate(Task) / isActive(Task) / resetActive(Task). Scheduler.isBlockingAllowed(Task) and Task.isBlockingAllowed() are removed.
  • Classic scheduler view: the command handlers blockTask / unblockTask are replaced by deactivateTask / activateTask (plus resetTaskActive); the classes moved from com.top_logic.util.sched.layout.block to com.top_logic.util.sched.layout.active. The table columns isBlocked, isEnabled and isBlockingAllowed are replaced by active. Application layouts referencing them must be adapted.
  • A customer component library replacing the React part FieldValue receives the optional props preformatted and action; the part list contains the part CopyButton.
  • Further removed API: the filters com.top_logic.util.sched.entry.filter.EnabledSchedulerEntryFilter and ManuallyStartedSchedulerEntryFilter, the classes BlockState and BlockStateRenderer (com.top_logic.util.sched.layout.block), TaskImpl.isBlockedByDefault(), and on SchedulerEntry the constructor SchedulerEntry(Task, boolean) and the methods setEnabled / isEnabled / setAttached / isAttached / createThread. Own layouts and code using them ask Scheduler.isActive(Task) instead or drop the usage.

Detect: grep -rn -e 'blockTask' -e 'unblockTask' -e 'isTaskBlocked' -e 'isTaskEnabled' -e 'isBlockingAllowed' -e 'sched.layout.block' -e 'isBlocked' -e 'FieldValue' -e 'SchedulerEntryFilter' -e 'BlockState' -e 'SchedulerEntry' .

5.7 ModificationListener and Committable (#30218, #30225)

  • ModificationListener.createModification(KnowledgeBase, CommitActions, Map created, Map updated, Map removed) becomes createModification(KnowledgeBase, CommitActions, PendingChanges changes); read the maps via changes.getCreated(), changes.getUpdated(), changes.getRemoved(). ModificationListener.notifyUpcomingDeletion(KnowledgeBase, CommitActions, KnowledgeItem) gets the CommitActions handle as additional second parameter (as createModification already does).
  • Committable.complete(CommitContext) is removed. Move such code to an AfterCommitAction (registered on the Transaction, or in a ModificationListener on the CommitActions handle; the action receives the UpdateEvent of the commit) and drop the @Override method.
  • An implementation of StorageImplementation that implements both ColumnStorage and SeparateTableStorage must override addChangeLocators(...) and canDetectValueChanges() to combine both behaviours (the compiler reports the conflicting default methods).

Detect: grep -rn -e 'ModificationListener' -e 'createModification' -e 'notifyUpcomingDeletion' -e 'complete(CommitContext' -e 'implements .*ColumnStorage' -e 'SeparateTableStorage' --include=*.java .

5.8 Background jobs and thread bindings (#30196, #30250)

  • ThreadLocalBinding, BindingRegistry and BindingSnapshot moved from com.top_logic.tl.ai.core.binding to com.top_logic.basic.thread. Adapt imports.
  • Cancelling a <start-job> job or stopping a scheduler task no longer interrupts the thread. Code relying on being woken up (own Thread.sleep, Object.wait, blocking I/O, Future.get) runs such waits with CancelScope.sleep(...) resp. CancelScope.runInterruptibly(...) — never with database accesses inside.
  • Own implementations of JobMonitor implement uncancelable() (a delegating implementation passes it to the wrapped monitor).
  • The record JobState has the additional component cancelable (before control); code constructing JobState (e.g. tests) passes it.
  • ViewActionChain.run(..., Consumer onComplete, Runnable onSettled) is gone; instead of the onSettled callback ViewActionChain.run(context, actions, arguments, after, onComplete, holds) takes a ViewActionChain.HoldObserver.

Detect: grep -rn -e 'tl.ai.core.binding' -e 'Thread.sleep' -e '\.wait(' -e 'JobMonitor' -e 'JobState' -e 'ViewActionChain.run' --include=*.java .

5.9 Login methods (#30067)

An own implementation of com.top_logic.base.accesscontrol.loginmethod.LoginMethod replaces getInitiationUrl(String returnToUrl) by getInitiationUrl(String returnToUrl, String windowName) and adds the window name (if not null) as parameter ExternalAuthenticationServlet.WINDOW_PARAM to the initiation URL, see Pac4jLoginMethod.

Detect: grep -rn -e 'LoginMethod' -e 'getInitiationUrl' --include=*.java .

5.10 Chat API (#30126)

Only for applications that address the chat API themselves or use tl-chat classes:

  • The JSON API /chat/* (ChatServlet) is gone; an own client calls the commands of the chat controls (name constants in ChatControl), e.g. via the headless access /agent-api.
  • ChatSessionRegistry: windowId(session, subSession), resolveSubSession(session, windowId) and resolveTracker(session, windowId, conversation) are gone. The tracker of a window is tracker(subSession, conversation, account); the window name is on the sub-session (subSession.getLayoutContext().getWindowId().getWindowName()).
  • TaskProgressTracker: get/setUserName, get/setWelcomeName and get/setWindowId are gone; getAccount() returns the tracker's account.
  • ChatViewBinding: writeSharedState, subjectOf, windowId, contextPath, userName, welcomeName, language and authenticated are gone (the state is written on every render by writeRenderState). presentable and presentableNames additionally expect the account of the run.
  • useTLCommand() and useTLUpload() (TypeScript) now return the command's answer (Promise<CommandResponse | undefined> instead of Promise<void>); callers ignoring the result are unchanged.

Detect: grep -rn -e '/chat/' -e 'ChatServlet' -e 'ChatSessionRegistry' -e 'TaskProgressTracker' -e 'ChatViewBinding' -e 'useTLCommand' -e 'useTLUpload' .

5.11 AI tools: commit messages (#30276)

  • Own Tool implementations calling TransactionScope.begin(ResKey): the method is gone; set the message on commit with TransactionScope.commit(ResKey).
  • An own writing tool may override commitMessage(arguments, context) (or set @ToolMethod(commitMessage = ...)) to describe its changes in the history; otherwise "Werkzeugaufruf <Name>" (tool call) appears.
  • Callers of ScriptToolStore.create / update additionally pass the commit message expression (null for none resp. unchanged).

Detect: grep -rn -e 'TransactionScope.begin' -e 'ScriptToolStore' -e '@ToolMethod' --include=*.java .

5.12 Self-service Java API (#30041, #30183, #30223)

  • Code constructing CreateAccountDialog passes no continuation command and passes an AccountRequest (e.g. new InvitationRequest(invitation)) instead of an Invitation.
  • CheckInvitationCode is renamed to CheckAccountCode.
  • LoginViewDialog.loginUserAndReload(...) now returns a HandlerResult: a caller passes an unsuccessful result (lock, maintenance mode) on to its caller instead of ignoring it.
  • Code calling sendInvitation for an invitation that is not open gets an error; an expired invitation is sent again with invitationResend.
  • Code calling InvitationModule.getInvitationMail(), getVerificationMail(), getResetPasswordMail() or getResetMFAMail() uses the operations sendInvitation, sendVerificationMail, sendResetPasswordMail or sendResetMFAMail instead.

Detect: grep -rn -e 'CreateAccountDialog' -e 'CheckInvitationCode' -e 'loginUserAndReload' -e 'sendInvitation' -e 'InvitationModule' --include=*.java .

5.13 React configuration editor: ConfigAnnotationControlProvider (#30266)

Only for applications with an own ConfigAnnotationControlProvider: a provider no longer refuses a property by returning null from createControl(...) but via accepts(ConfigFieldModel model, A annotation). createControl is only called for accepted properties and must not return null. Move the former null condition into an override of accepts (e.g. return ConfigControlService.holdsText(model);). If the field should take the whole line (multi-line content), also override rendersWholeLine(ConfigFieldModel, A) with true.

Detect: grep -rn 'ConfigAnnotationControlProvider' --include=*.java .

5.14 TL-Script: try() passes a ResKey to catch (#30265)

The catch function of try() always receives the error message as ResKey, no longer as string. No change is needed for display and log(). A script that processes the message as string (length, regular expression, comparison with a string, concatenation) converts it with localize() first:

try(..., catch: msg -> $msg.localize().contains("..."))

Detect: grep -rn 'catch:' --include=*.xml --include=*.script --include=*.tls . (and scripts stored in the database, e.g. via the model editor)

5.15 Scripted tests: LastRevisionNamingScheme (#29847)

A script that assigns LastRevisionNamingScheme after a commit of another user than the one executing the assignment must execute the assignment as the committing user (user-id of the SetGlobalVariableOp).

Detect: grep -rln 'LastRevisionNamingScheme' --include=*.script.xml .

5.16 Sorting helpers of React table columns (#30204)

ColumnProviderService.labelOrder(), ColumnProviderService.unreadableLast(Comparator) and ScriptedFilter.order(Comparator) are removed. Own Java code building the sort of a React table column with them uses ColumnProviderService.cellSort(...) instead.

Detect: grep -rn -e 'labelOrder' -e 'unreadableLast' -e 'ScriptedFilter' --include=*.java .

5.17 OpenApiServlet has a new super class (#30177)

OpenApiServlet now extends NoContextServlet instead of AbstractTopLogicServlet; the method serviceWithLogMark(HttpServletRequest, HttpServletResponse) is removed. An own subclass of OpenApiServlet overrides doService(HttpServletRequest, HttpServletResponse) instead.

Detect: grep -rn -e 'extends OpenApiServlet' -e 'serviceWithLogMark' --include=*.java .


6. Stylesheets and themes

6.1 Theme role surface-interactive-selected renamed (#30000)

--tl-surface-interactive-selected → --tl-surface-interactive-strong (neutral grey, no longer a selection). There is no alias. Stylesheets and MUI themes that drew a selection with the old name read the new selection roles instead:

Old New
--tl-surface-interactive-selected (neutral strong fill: track, skeleton, pill, avatar, drop target, field button hover) --tl-surface-interactive-strong
--tl-surface-interactive-selected (selected row, node, item) --tl-surface-selected, hover --tl-surface-selected-hover, edge --tl-border-selected

New roles: surface-selected, surface-selected-hover, border-selected. Customer themes get the selection roles computed by derive; MUI themes feed MUI's selected state into surface-selected. The complete table is in the design system repository, docs/migration/altlast.md.

Detect: grep -rn 'surface-interactive-selected' --include=*.css --include=*.ts --include=*.tsx --include=*.js --include=*.json --include=*.xml .


7. Further notes (no action required)

  • Duplicate links (#30031): the automatic migration logs every link it removed or shortened; read the migration log after the first start.
  • Agent screenshots (#30054): screenshots from before this release belong to no chat; the data migration deletes them.
  • Mandatory columns in migrations (#30167): adding a mandatory column to a large table now takes longer (measured on PostgreSQL 15: about 12 s per million rows) and needs about twice the table's space until the next VACUUM. Database defaults already present stay untouched. In the UI, default-value of AddDatabaseColumnProcessor is labelled "Fill value"; the configuration name is unchanged.
  • Dynamic visibility (#30205): an attribute whose dynamic visibility yields mandatory now prevents saving a React form or row-edit table with an empty value; with read-only, disabled, hidden or editable an attribute declared mandatory in the model is no longer checked as mandatory.
  • Security parents of structured elements (#30079): StructuredElementWrapper no longer overrides getSecurityParents(), so configured security parents now also apply to structured elements; Document returns its containing WebFolder in addition to the configured security parents; a deleted object has no security parents. Users may inherit roles they did not have before — check the access rights of structured types with a configured security parent.
  • Claude 5.5 workarounds (#30251): workarounds in <model-overrides> (e.g. reasoning="false" for claude-haiku-5-5) can be removed.
  • Self-service mails (#30223): a self-service mail that cannot be sent is reported as warning in the application log; the default scripts no longer show the English failure text to the user.
  • Tables (#30227): new theme role surface-layer-muted and typography class overline; separators in dark mode are slightly stronger, the table body has no column lines.
  • Commit and rollback (#30259): after a successful database commit, Committable.rollback() is no longer called; an exception of a synchronous UpdateListener is logged and no longer thrown out of commit(). Flags in own committables that guarded against this are no longer needed.
  • Access check for another person (#30269): ModelAccessRights.isAllowed(Person, ...) decides by the given person, not by the bypass or admin state of the current context. A workaround switching the identity before the check is no longer needed; code that relied on the old bypass now gets false.
  • Hidden instead of disabled (#30025): React commands and units for which the user holds the needed role on no object are hidden instead of disabled.
  • Executability rules first (#30211): configured <executability> rules of a React command now apply before <access> and the built-in rules (e.g. of <delete-object>); clique="delete" makes a command destructive (red confirmation in <confirm>).
  • Busy indicator (#30237): a command unanswered for longer than busy-delay (default 500 ms, ViewConfig) blocks the page with a busy indicator. Own React controls sending requests next to the input (suggestions, lazy loading) should set the option background (BACKGROUND_COMMAND), otherwise the page blocks during these requests.
  • Slots (#30045): a <slot-content> that reaches no <slot> is logged as warning (diagnosis unrouted); set optional="true" for content that has a slot only in some shells. Panes of split panels and sidebar entries have slot positions of their own, which can change the routing.
  • /agent-api without login (#30125): a request without valid credential gets 401 with JSON instead of a redirect to the login page.
  • Session ID on login (#30069): SessionService.loginUser changes the session ID on every login with an existing HTTP session (protection against session fixation), also for an external login.
  • Generic OpenID login (#30179): the button of the generic OpenID client shows tl_pac_label (by default a generic "single sign-on" label instead of the technical client name). An existing useSSO application may add pac4j-generic.xml to its metaConf.txt to have the login on the development server too (optional).

8. Verification checklist

  1. mvn clean install of the application (with tests if possible).
  2. Start the application against a copy of the production database first: the migrations of #30031 (unique link tables), #30079 (comments deleted) and #30167 (column handling) run at startup.
  3. Read the startup and migration log: no configuration errors (blocked-by-default, group-mapper, user-mapping attributes, tool access policy with unknown models), no errors of Ticket_30031_unique_link_tables.
  4. Log in, open /view/ (shell overlay, #30044) and every React view; open the classic layouts with scheduler, self-service and search.
  5. Check styling of selected rows, nodes and items in own stylesheets / MUI themes (#30000) and row, cell and display colours (#30256).
  6. Check access: users without admin rights see the expected units and commands; role rules over contacts (#30096) and security parents of structured types (#30079) give the expected rights.
  7. Report to the user every step that was skipped, needed a decision, or touches data (deleted comments, removed duplicate links, changed access rights).
  • Get Started
  • Github
  • Discord
  • Das Unternehmen hinter TopLogic
  • Softwareentwicklung heute
  • Kontakt

© Copyright – Business Operation Systems GmbH

  • top-logic.com
  • Nutzungsbedingungen
  • Impressum
  • Rechtlicher Hinweis
  • Datenschutz
  • EN
  • Login