Skip to content

schedule the removal of legacy Cylc 7 configuration support #7278

Description

@oliver-sanders

The removal of Cylc 7 compatibility mode is covered by #6849, however, there are other deprecated Cylc 7 things still lingering, namely:

  1. Deprecated configurations (e.g. [cylc][parameters] -> [task parameters]).
  2. Deprecated event handler template variables (e.g, %(suite)s).
  3. Deprecated environment variables (e.g, $CYLC_SUITE_NAME).
  4. Deprecated host selection logic (i.e, [job]batch system and [remote]host).
  5. Deprecated suite_state xtrigger.

Further to #7274 we need to agree on a schedule for the removal of these things.

With Cylc 7 compat mode being withdrawn in 8.7, we should delay removal until at least 8.8 to ease the transition for compat-mode workflows.

Furthermore, we should give one full version's worth or warning (i.e, upgrade deprecation warnings to errors and change the message to read "support will be removed at .").

(Also, realistically, we don't have much resource to spare right now!)

So, my suggestion would be 8.9:

  • 8.7 - drop compat mode & add "support will be removed in Cylc 8.9" to config deprecation warnings.
  • 8.8 - warnings -> errors (i.e, the colour of the messages will change, but you'll still be able to run the workflow).
  • 8.9 - removal of support for deprecated syntax.

[edit 2026-06-26] go with the proposed dates above

Will amend REMOVE AT versions and link any relevant BACK COMPAT comments to this issue once decided.

Activity

  1. modified the milestones: 8.7.0, 8.6.x on Apr 15, 2026
  2. added
    questionFlag this as a question for the next Cylc project meeting.
    on Jun 11, 2026
  3. oliver-sanders commented on Jun 11, 2026

    @oliver-sanders
    MemberAuthor

    Deprecated workflow configs:

    u.deprecate(
    '8.0.0',
    ['cylc', 'task event mail interval'],
    ['cylc', 'mail', 'task event batch interval'],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    u.deprecate(
    '8.0.0',
    ['runtime', '__MANY__', 'suite state polling'],
    ['runtime', '__MANY__', 'workflow state polling'],
    silent=cylc.flow.flags.cylc7_back_compat,
    is_section=True,
    )
    u.obsolete(
    '8.0.0', ['runtime', '__MANY__', 'workflow state polling', 'host'])
    u.obsolete(
    '8.0.0', ['runtime', '__MANY__', 'workflow state polling', 'user'])
    u.deprecate(
    '8.3.0',
    ['runtime', '__MANY__', 'workflow state polling', 'run-dir'],
    ['runtime', '__MANY__', 'workflow state polling', 'alt-cylc-run-dir'],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    u.deprecate(
    '8.0.0',
    ['cylc', 'parameters'],
    ['task parameters'],
    silent=cylc.flow.flags.cylc7_back_compat,
    is_section=True,
    )
    u.deprecate(
    '8.0.0',
    ['cylc', 'parameter templates'],
    ['task parameters', 'templates'],
    silent=cylc.flow.flags.cylc7_back_compat,
    is_section=True,
    )
    # Whole workflow task mail settings
    for mail_setting in ['to', 'from', 'footer']:
    u.deprecate(
    '8.0.0',
    ['cylc', 'events', f'mail {mail_setting}'],
    ['cylc', 'mail', mail_setting],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    # Task mail settings in [runtime][TASK]
    for mail_setting in ['to', 'from']:
    u.deprecate(
    '8.0.0',
    ['runtime', '__MANY__', 'events', f'mail {mail_setting}'],
    ['runtime', '__MANY__', 'mail', mail_setting],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    u.deprecate(
    '8.0.0',
    ['cylc', 'events', 'mail smtp'],
    None, # This is really a .obsolete(), just with a custom message
    cvtr=converter(lambda x: x, (
    'DELETED (OBSOLETE) - use "global.cylc[scheduler][mail]smtp" '
    'instead')
    ),
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    u.deprecate(
    '8.0.0',
    ['runtime', '__MANY__', 'events', 'mail smtp'],
    None,
    cvtr=converter(lambda x: x, (
    'DELETED (OBSOLETE) - use "global.cylc[scheduler][mail]smtp" '
    'instead')
    ),
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    u.deprecate(
    '8.0.0',
    ['scheduling', 'max active cycle points'],
    ['scheduling', 'runahead limit'],
    cvtr=converter(
    lambda x: f'P{int(x) - 1}' if x != '' else '',
    '"{old}" -> "{new}"'
    ),
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    u.deprecate(
    '8.0.0',
    ['scheduling', 'hold after point'],
    ['scheduling', 'hold after cycle point'],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    for job_setting in [
    'execution polling intervals',
    'execution retry delays',
    'execution time limit',
    'submission polling intervals',
    'submission retry delays'
    ]:
    u.deprecate(
    '8.0.0',
    ['runtime', '__MANY__', 'job', job_setting],
    ['runtime', '__MANY__', job_setting],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    # Workflow timeout is now measured from start of run.
    # The old timeout was measured from start of stall.
    for old, new in [
    ('timeout', 'stall timeout'),
    ('abort on timeout', 'abort on stall timeout'),
    ('inactivity', 'inactivity timeout'),
    ('abort on inactivity', 'abort on inactivity timeout'),
    ('startup handler', 'startup handlers'),
    ('shutdown handler', 'shutdown handlers'),
    ('timeout handler', 'stall timeout handlers'),
    ('stalled handler', 'stall handlers'),
    ('aborted handler', 'abort handlers'),
    ('inactivity handler', 'inactivity timeout handlers'),
    ]:
    u.deprecate(
    '8.0.0',
    ['cylc', 'events', old],
    ['cylc', 'events', new],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    for old in [
    "expired handler",
    "late handler",
    "submitted handler",
    "started handler",
    "succeeded handler",
    "failed handler",
    "submission failed handler",
    "warning handler",
    "critical handler",
    "retry handler",
    "submission retry handler",
    "execution timeout handler",
    "submission timeout handler",
    "custom handler"
    ]:
    u.deprecate(
    '8.0.0',
    ['runtime', '__MANY__', 'events', old],
    ['runtime', '__MANY__', 'events', f"{old}s"],
    silent=cylc.flow.flags.cylc7_back_compat,
    )
    for old in [
    'abort on stalled',
    'abort if startup handler fails',
    'abort if shutdown handler fails',
    'abort if timeout handler fails',
    'abort if inactivity handler fails',
    'abort if stalled handler fails',
    ]:
    u.obsolete('8.0.0', ['cylc', 'events', old])
    u.deprecate(
    '8.0.0',
    ['cylc'],
    ['scheduler'],
    silent=cylc.flow.flags.cylc7_back_compat,
    is_section=True,
    )

    def upgrade_graph_section(cfg: Dict[str, Any], descr: str) -> None:
    """Upgrade Cylc 7 `[scheduling][dependencies][X]graph` format to
    `[scheduling][graph]X`."""
    # Parsec upgrader cannot do this type of move
    with contextlib.suppress(KeyError):
    note = ''
    if 'dependencies' in cfg['scheduling']:
    if ['graph'] == cfg['scheduling']['dependencies'].keys():
    msg_old = '[scheduling][dependencies]graph'
    msg_new = '[scheduling][graph]R1'
    list_cp = False
    else:
    note = (
    '\n ([scheduling][dependencies]graph moves to'
    ' [scheduling][graph]R1)'
    if 'graph' in cfg['scheduling']['dependencies'] else '')
    msg_old = '[scheduling][dependencies][X]graph'
    msg_new = '[scheduling][graph]X - for X in:'
    list_cp = True
    if 'graph' in cfg['scheduling']:
    raise UpgradeError(
    f'Cannot upgrade deprecated item "{msg_old} -> {msg_new}" '
    f'because {msg_new[:-1]} already exists.'
    )
    else:
    keys: Set[str] = set()
    cfg['scheduling'].setdefault('graph', {})
    cfg['scheduling']['graph'].update(
    cfg['scheduling'].pop('dependencies')
    )
    graphdict: Dict[str, Any] = cfg['scheduling']['graph']
    for key, value in graphdict.copy().items():
    if isinstance(value, dict) and 'graph' in value:
    graphdict[key] = value['graph']
    keys.add(key)
    elif key == 'graph' and isinstance(value, str):
    graphdict[key] = value
    keys.add(key)
    if keys and not cylc.flow.flags.cylc7_back_compat:
    msg = (
    'graph items were automatically upgraded '
    f'in "{descr}":\n'
    f' * (8.0.0) {msg_old} -> {msg_new}'
    )
    if list_cp:
    msg += f"\n {', '.join(sorted(keys))}"
    LOG.warning(msg + note)
    def upgrade_param_env_templates(cfg, descr):
    """Prepend contents of `[runtime][X][parameter environment templates]` to
    `[runtime][X][environment]`."""
    if 'runtime' in cfg:
    dep = '[runtime][%s][parameter environment templates]'
    new = '[runtime][%s][environment]'
    first_warn = True
    for task_name, task_items in cfg['runtime'].items():
    if 'parameter environment templates' not in task_items:
    continue
    if not cylc.flow.flags.cylc7_back_compat:
    if first_warn:
    LOG.warning(upgrader.depr_msg)
    first_warn = False
    LOG.warning(
    f' * (8.0.0) {dep % task_name} contents prepended to '
    f'{new % task_name}'
    )
    for key, val in reversed(
    task_items['parameter environment templates'].items()):
    if 'environment' in task_items:
    if key in task_items['environment']:
    LOG.warning(
    f' *** {dep % task_name} {key} ignored as {key} '
    f'already exists in {new % task_name}'
    )
    continue
    else:
    task_items['environment'] = OrderedDictWithDefaults()
    task_items['environment'].prepend(key, val)
    task_items.pop('parameter environment templates')
    def warn_about_depr_platform(cfg):
    """Validate platforms config.
    - Warn if deprecated host or batch system appear in config.
    - Raise if platforms section is also present.
    - Or raise if using invalid subshell syntax for platform def.
    """
    if 'runtime' not in cfg:
    return
    for task_name, task_cfg in cfg['runtime'].items():
    if 'platform' in task_cfg and task_cfg['platform']:
    fail_if_platform_and_host_conflict(task_cfg, task_name)
    # Fail if backticks subshell e.g. platform = `foo`:
    is_platform_definition_subshell(task_cfg['platform'])
    elif not cylc.flow.flags.cylc7_back_compat:
    depr = get_platform_deprecated_settings(task_cfg, task_name)
    if depr:
    msg = "\n".join(depr)
    LOG.warning(
    "deprecated settings found "
    f"(please replace with [runtime][{task_name}]platform):"
    f"\n{msg}"
    )

    Deprecated global configs:

    None.

  4. oliver-sanders commented on Jun 11, 2026

    @oliver-sanders
    MemberAuthor

    (announced on developer chat)

  5. oliver-sanders commented on Jun 26, 2026

    @oliver-sanders
    MemberAuthor

    (announced on developer chat)

    Three thumbs up and no objections, removing the question label.

  6. removed
    questionFlag this as a question for the next Cylc project meeting.
    on Jun 26, 2026
  7. modified the milestones: 8.8.0, 8.7.0 on Jun 26, 2026
  8. oliver-sanders commented on Jun 26, 2026

    @oliver-sanders
    MemberAuthor
  9. modified the milestones: 8.7.0, 8.9.0 on Jul 6, 2026
  10. MetRonnie commented on Jul 16, 2026

    @MetRonnie
    Member

    Should the suite_state xtrigger be added to this list?

  11. oliver-sanders commented on Jul 17, 2026

    @oliver-sanders
    MemberAuthor

    Should the suite_state xtrigger be added to this list?

    Reasonable, added (the above list might not be exhaustive).

  12. modified the milestones: 8.9.0, 8.7.x on Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions