

# WDL engine change log for HealthOmics
<a name="wdl-engine-change-log"></a>

This page contains change log for WDL engine for both WDL strict mode and WDL lenient mode. Each section summarizes new features, enhancements, and deprecations.

## New features and enhancements
<a name="wdl-changelog-new-features"></a>

The following table lists new features and enhancements for the WDL engine on HealthOmics.


| Date of Change | Change Name | Change Description | Recommended Action | 
| --- | --- | --- | --- | 
| September 22, 2026 | Consistent CRLF line-ending handling | HealthOmics trims Windows-style (`\r\n`) line endings consistently across `read_lines()`, `read_tsv()`, and `read_map()`, matching the behavior of standard Linux tools. | None. There are no workflow changes required. | 
| September 22, 2026 | `zip()`, `cross()`, and `read_tsv()` now correctly handle mixed-type inputs. manual type-cast workarounds can be removed. | The built-in functions `zip()`, `cross()`, and `read_tsv()` now correctly infer the types of their return values. In previous versions, these functions could produce unexpected type errors when combining mixed types (for example, `Array[String]` and `Array[Int]` in a `zip()` call), requiring manual type-cast workarounds in WDL code. | If your workflow includes explicit type casts added to work around type errors from `zip()`, `cross()`, or `read_tsv()`, you can remove them. HealthOmics now infers the correct types automatically.<br />Keeping existing type casts will not cause any errors or affect workflow behavior. | 
| September 22, 2026 | `basename(path, "")` fix returns the filename instead of and empty string. | `basename(path, "")` now returns the file name.<br />For example, `basename("/path/to/file.txt", "")` will return `"file.txt"` instead of an empty string. | Search your workflows for `basename(x, "")` calls. If any exist, verify that returning the filename is the expected behavior. | 
| September 22, 2026 | Float-key collision detection in `collect_by_key()` | `collect_by_key()` previously treated `Float` keys as identical if they matched to 6 decimal places. Distinct values such as `0.0000001` and `0.0000002` were merged into one group.<br />From the change date onwards, HealthOmics detects this collision at workflow creation time and fail the workflow with a clear error in GetWorkflow statusMessage field instead of discarding results.<br />A workflow that previously completed successfully while merging distinct `Float` keys will now fail with a collision detection error like `"statusMessage": "(collect_by_key_test.wdl Ln 17 Col 47) No such function: collect_by_key\n Map[Float, Array[String]] collected = collect_by_key(pairs_with_collision)"` | You can use any of the three approaches:+  Use `String` keys: Change `Array[Pair[Float, String]]` to `Array[Pair[String, String]]` and quote your key values. String keys are always distinct and eliminate the collision risk entirely. <br />+  Use `Int` keys: Change the key type to `Array[Pair[Int, String]]` Multiply your float values by a power of 10 to preserve the required precision as integers. <br />+  If `Float` keys are unavoidable, round them to a consistent precision first to ensure each key is truly distinct and no data is silently merged. Normalize each key using `round(value * 1000000.0) / 1000000.0` before passing to `collect_by_key()`.  | 
| Upcoming (60 days notice from Sep 22, 2026) | Float division (/) will return decimals, not truncated integers. | **Current behavior:** The `/` operator on `Float` operands returns nearest integer. For example, `5.0 / 2` returns `2.0`, not `2.5`. If both operands are `Int` (for example, `5 / 2`), the result returns 2.<br />**Upcoming change (Nov 30, 2026):** The float division on float operands will return with 16 decimal precision. After this date, `5.0 / 2.0` will return `2.5`, `150.0 / 7.0` will return `21.428...` instead of an integer value.<br />The precision of the result depends on how it is consumed: if the result type is `Float`, the value is computed up to 16 decimal digits of precision; if the result is coerced to a `String` it is formatted to 6 decimal digits instead.<br />For example, `5.0 / 2.0` will return `2.5`, and `150.0 / 7.0` will return `21.4285714285714` as a `Float`, but `21.428571` if interpolated into a `String`.<br />`Int / Int` expressions such as `5 / 2` are unaffected and will continue to return 2. | To ensure integer results regardless of engine version, wrap float division in `floor()` or `ceil()`:+  `floor(5.0 / 2.0)` → always returns `2` <br />+  `ceil(5.0 / 2.0)` → always returns `3` <br />If your workflow intentionally expects decimal results from float division, no changes are needed. The upcoming division behavior from Nov 30,2026 will return decimal places also.<br />Note: `5/2` will still return 2 as the `/` operator is applied on integers. | 