Skip to content

Declare the jev config scope so jev.* settings are recognized - #2

Merged
pditommaso merged 3 commits into
masterfrom
cursor/jev-config-scope-666e
Oct 12, 2026
Merged

pditommaso merged 3 commits into
masterfrom
cursor/jev-config-scope-666e

Conversation

@edmundmiller

@edmundmiller edmundmiller commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Issue

Nextflow logs Unrecognized config option for every jev.* setting. The plugin reads those options, but it never declared a config scope. Flagged on #1 (#1 (review)); it predates that PR.

Fix

JevConfig is the scope, the way TowerConfig and AwsConfig are: it implements ConfigScope, with @ScopeName('jev'), @Description, and a @ConfigOption on each field it already reads (provider, apiKey, model, endpoint, timeout, cacheDir). The no-arg constructor is empty. The (Map, env) constructor is unchanged. The class is registered in extensionPoints. provider is a JevProvider with types=[String]; timeout stays java.time.Duration in whole seconds, with types=[Integer].

Where

  • src/main/groovy/nextflowio/plugin/JevConfig.groovy
  • build.gradle (extensionPoints)
  • src/test/groovy/nextflowio/plugin/JevConfigTest.groovy reads the generated META-INF/spec.json and requires the jev scope and its options, so a missing extensionPoints entry fails the test

Followed, at the plugin's Nextflow 25.10.0:

Open in Web Open in Cursor 

The plugin reads jev.provider, apiKey, model, endpoint, timeout and
cacheDir but never registered a ConfigScope, so Nextflow logged
Unrecognized config option for each of them. JevScope declares those
options with the same names, types and defaults, and is registered as
an extension point.

Co-authored-by: Edmund Miller <edmundmiller@users.noreply.github.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-09T13:36:05.173575Z 96eddff PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pditommaso pditommaso left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this fixes the warning: the jev scope ends up in META-INF/spec.json, and in 25.10.0 plugins are loaded before the config is validated, so jev.* is recognized.

I'd rather not add a second class next to JevConfig, though. JevScope repeats the option list, copies the defaults into its descriptions, and its JevScope(Map) constructor and fields are never used at runtime (Nextflow only reflects the declared fields and calls the no-arg constructor). In particular:

  • JevScope() calls this([:]), which runs all of JevConfig's resolution whenever the extension is loaded. If that ever throws, the extension is skipped without an error and the warnings come back.
  • A new option added only to JevConfig would pass JevScopeTest, which hard-codes the same six names, and users would get "Unrecognized config option" again.
  • The descriptions hard-code typesafe/jev-1.13, so the next OpenRouter snapshot bump leaves the published spec out of date.
  • apiKey is a public property here but private behind a failing getter in JevConfig, and jev.apiKey = '' is handled differently by the two classes.

Could JevConfig declare the scope itself instead? That's how TowerConfig, AwsConfig, WaveConfig and K8sConfig do it: one class is the extension point, with an empty constructor for Nextflow and the (Map, env) constructor for the plugin.

@ScopeName('jev')
@Description('''
    The `jev` scope controls how the nf-jev plugin calls a Jev decisions endpoint.
''')
@CompileStatic
class JevConfig implements ConfigScope {

    @ConfigOption(types=[String])
    @Description('''
        The service that answers requests: `typesafe` (default) or `openrouter`.
        Inferred from the host of `endpoint` when unset.
    ''')
    final JevProvider provider

    @ConfigOption
    @Description('''
        The credential for the selected provider (default: the `TYPESAFE_API_KEY`
        or `OPENROUTER_API_KEY` environment variable, matching the provider).
    ''')
    private final String apiKey

    @ConfigOption
    @Description('''
        The Jev model id (default: the selected provider's current model).
    ''')
    final String model

    // endpoint, cacheDir: same pattern

    @ConfigOption(types=[Integer])
    @Description('''
        The request timeout, in seconds (default: `30`).
    ''')
    final Duration timeout

    /* required by extension point -- do not remove */
    JevConfig() {}

    JevConfig(Map opts, Map<String,String> env) {
        // unchanged
    }
}

Then register 'nextflowio.plugin.JevConfig' in extensionPoints, drop JevScope and revert the AGENTS.md/SPEC.md layout changes. JevExtension and JevClient stay as they are.

A few details:

  • types=[String] / types=[Integer] describe what users actually write for the enum and java.time.Duration fields. AwsS3Config.s3Acl does the same for an enum. In 25.10.0 the spec still records the field's Java type and validation only checks that the name exists; newer Nextflow adds the types entries.
  • Please keep timeout in whole seconds and don't switch the field to nextflow.util.Duration. That class reads a bare number as milliseconds, so an existing jev.timeout = 30 would become 30 ms.
  • Private fields are fine for @ConfigOption, because the spec uses getDeclaredFields.
  • For the test, check that the jev scope and its options are in the generated META-INF/spec.json, or at least compare SpecNode.Scope.of(JevConfig, …) with the expected names. Right now nothing fails if the extensionPoints entry is removed.

Drop JevScope. JevConfig is the extension point: an empty constructor
for Nextflow, and the existing (Map, env) constructor for the plugin.
The spec test reads META-INF/spec.json, so a missing extensionPoints
entry fails.

Co-authored-by: Edmund Miller <edmundmiller@users.noreply.github.com>
@cursor

cursor Bot commented Oct 11, 2026

Copy link
Copy Markdown

Addressed in ebcaa31.

JevConfig is the scope now, the same way TowerConfig is: @ScopeName('jev'), @ConfigOption on the fields it already reads, an empty constructor, and 'nextflowio.plugin.JevConfig' in extensionPoints. JevScope is gone, and the AGENTS.md / SPEC.md layout notes are back to how they were. JevExtension and JevClient are unchanged.

  • The empty constructor does not call this([:]), so loading the extension point does not run resolution.
  • provider stays a JevProvider with @ConfigOption(types=[String]). timeout stays java.time.Duration (whole seconds via Duration.ofSeconds) with @ConfigOption(types=[Integer]). apiKey stays private behind the failing getter, so jev.apiKey = '' is still only handled in this class.
  • Descriptions name the provider default rather than a model id, so they do not hard-code typesafe/jev-1.13.
  • JevConfigTest reads the generated META-INF/spec.json and requires a jev scope with those six options. Removing the extensionPoints entry drops the scope from that file and the test fails.

Unit tests passed, and guardrail and route passed. label-samples failed
while the ENA filereport endpoint was returning 500; it answers 200 again.

Co-authored-by: Edmund Miller <edmundmiller@users.noreply.github.com>

@pditommaso pditommaso left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this addresses the previous review: JevConfig is now the single jev scope and extension point, timeout stays in whole seconds, and the spec test fails if the extensionPoints entry is removed (verified locally). Tests and CI are green.

Minor, non-blocking: the !contains('jev-1.13') check in the spec test only guards one snapshot id and could be dropped. Will squash-merge so the JevScope commit message doesn't land on master.

@pditommaso

Copy link
Copy Markdown
Member

Thanks @edmundmiller, all good. Approved, will merge shortly.

@pditommaso
pditommaso merged commit d5a0cf3 into master Oct 12, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants