Skip to content

build: move openjd-model, openjd-sessions and openjd-cli (Python) into openjd-rs - #433

Closed
crowecawcaw wants to merge 785 commits into
OpenJobDescription:mainfrom
crowecawcaw:python-monorepo
Closed

crowecawcaw wants to merge 785 commits into
OpenJobDescription:mainfrom
crowecawcaw:python-monorepo

Conversation

@crowecawcaw

@crowecawcaw crowecawcaw commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Fixes: none

Important

Before merging

  1. Merge with a merge commit, not squash. The first three commits import the full history of openjd-model-for-python, openjd-sessions-for-python and openjd-cli. The branch-protection ruleset allows only squash merges and requires linear history, so landing this with history needs an admin to bypass the ruleset (or relax it) for this one PR. Squashing works, but the imported history is lost and git blame under python/ points at the squash commit.
  2. DCO: 25 of the 776 imported commits have no Signed-off-by (7 model, 10 sessions, 8 cli). They're other people's historical commits, so this PR doesn't rewrite them. A maintainer can mark DCO as passing for them. Every new commit in this PR is signed off.
  3. Merging releases openjd-model 0.14.0, openjd-sessions 0.13.1 and openjd-cli 0.7.8 to PyPI. Before merging, set up PyPI trusted publishing and the signing secrets (see "One-time setup" below). Otherwise the PyPI step fails; it can be re-run after the setup.

What was the problem/requirement? (What/Why)

OpenJD development is centered here, but the Python packages live in three other repositories, and each has its own release process. A Rust change that Python users should get takes a chain of releases and PRs:

  1. openjd-rs releases the crates.
  2. openjd-model-for-python bumps the crates.io pins in its bindings and releases.
  3. openjd-sessions-for-python bumps its openjd-model pin and releases.
  4. openjd-cli bumps both pins and releases.

The pins drift along the way. On mainline today, openjd-cli still requires openjd-model < 0.12 and openjd-sessions < 0.13, while both are at 0.13.0.

What was the solution? (How)

Move the three Python packages into this repository with their history, build and test them from the Cargo workspace, and release them with the same release-plz Release PR as the crates.

Layout

  • python/openjd-model, python/openjd-sessions and python/openjd-cli are the old repositories, imported with git filter-repo:
    • Paths are moved under python/<pkg>/.
    • Old tags are renamed to legacy/<repository>/<version>. Push them separately if you want them upstream (git push origin 'refs/tags/legacy/*'); release-plz ignores them.
    • (#123) PR references are rewritten to OpenJobDescription/<repository>#123, so they don't link to the wrong PRs here.
  • openjd-model's PyO3 crate, now openjd-model-py, is a workspace member. It depends on openjd-expr, openjd-model and openjd-sessions by path instead of from crates.io. This removes the bindings' nested Cargo workspace, its Cargo.lock and deny.toml, and the [patch.crates-io] dance.
  • openjd-sessions and openjd-cli each get an empty publish = false "release anchor" crate (openjd-sessions-py, openjd-cli-py), so release-plz can version them.
  • The python/ crates aren't in default-members, so plain cargo build/cargo test still cover only the Rust crates.

Versions

  • Each Python package's version is the version in its Cargo.toml. maturin reads it natively, hatch reads it with a regex, and _version.py reads it back from the installed package's metadata.
  • That removes the in-tree PEP 517 backend, generate_version.py, maturin_build.py and the hatch VCS/copy hooks.

Releasing (release-plz.toml, release-plz.yml)

  • The Python packages are git_only: their last release comes from a python-<distribution>-v<version> tag, and they're never published to crates.io. release-plz bumps them from conventional commits that touch their directories.
  • Cargo dependencies between the packages (bindings → crates, sessions → model, cli → sessions + model) cascade the bumps. A release of openjd-expr, openjd-model or openjd-sessions therefore releases openjd-model too, and then the packages that depend on it, in the same Release PR.
  • release-plz doesn't touch pyproject.toml. After it updates the Release PR, the workflow runs the new scripts/sync_python_pins.py on the PR branch to move the pins between the Python packages.
  • When a Python tag is created, the workflow:
    • builds the six-platform abi3 openjd-model wheels and sdist, and the openjd-sessions/openjd-cli wheel and sdist, from the tag;
    • PGP-signs every file the way the old repositories did;
    • attaches the files and signatures to the GitHub Release;
    • uploads to PyPI in dependency order.
  • The Release PR job now waits for the release job, so it's computed from the tags that job creates (see testing).

CI

  • New python.yml runs hatch run lint, hatch build and hatch run test for each package on Linux, Windows and macOS. Each package keeps the Python versions it was tested on before: 3.9–3.14, and 3.9–3.12 for cli.
  • Each package is tested against the in-repo versions of the others. The bindings build from the in-repo crates, and the sessions/cli hatch environments install the in-repo packages first.
  • The workflow also runs the sessions macOS cross-user tests, the openjd-model THIRD-PARTY-LICENSES.txt check, and sync_python_pins.py --check. It's path-filtered to python/**, crates/openjd-{expr,model,sessions}/**, Cargo files and the workflow itself.
  • CodeQL now also analyzes Python, and Dependabot covers the packages' pip requirements.

What is the impact of this change?

  • One PR can change a crate and the Python bindings that expose it, and CI tests them together. If a crate change alters what Python users see, the Python tests fail in that PR. This change hit that case twice; see "Is this a breaking change?".
  • One Release PR releases everything that changed, and the pins between Python packages stay in step automatically.
  • PyPI package names, import names and public APIs are unchanged. Downstream pins keep working.
  • After this merges, the three old repositories can be archived with a pointer to python/ here, and their open issues moved with gh issue transfer. Their open PRs are Dependabot bumps and two draft sessions PRs (fix(sessions): prune exited environment variables from WrappedAction.Environment #362, docs(expr): warn that is_relative_to is a lexical prefix test, not containment #367), which would need re-opening here.

One-time setup (also in RELEASING.md)

  • On PyPI, add a trusted publisher to each of openjd-model, openjd-sessions and openjd-cli: repository OpenJobDescription/openjd-rs, workflow release-plz.yml, environment release.
  • The release environment needs AWS_PGP_KEY_SECRET_ROLE, AWS_PGP_KEY_SECRET and PGP_USER, as the old repositories had.

How was this change tested?

  • Locally (macOS arm64, Python 3.14; mypy also on 3.9):
    • openjd-model: 6,323 tests pass.
    • openjd-sessions: 1,015 tests pass.
    • openjd-cli: 318 tests pass, with its pins moved to the 0.13 releases (from < 0.12/< 0.13).
    • hatch run lint is clean for all three.
    • cargo clippy --workspace --all-targets -D warnings, cargo +nightly fmt --check, cargo doc -D warnings and cargo deny check pass.
    • Wheels build from the sdist alone; maturin copies the path-dependency crates into the sdist.
  • Fork CI (crowecawcaw/openjd-rs#4):
    • Every ci.yml job passes, including conformance and cross-user on all three OSes, so adding the PyO3 crate to the workspace doesn't affect them.
    • The full Python matrix passes. One Windows/3.12 leg hit a PowerShell-subprocess timeout and passed on re-run; the old model repo's Windows legs flake the same way.
    • The CodeQL check flags only alerts already present in the imported Python code (6 errors, all in files this PR doesn't touch). Every alert is "new" to this repo.
  • Release flow, run end to end on the fork's main with a fork-only commit that points the repository guards at the fork, skips the crates, and drops signing and PyPI (which need upstream secrets):
    • The merge created the three python-* tags and GitHub Releases on the merge commit, with the changelog entries as release notes.
    • All six openjd-model wheels, the sdist and the sessions/cli artifacts were built and attached to the releases.
    • Installing the released 0.14.0/0.13.1/0.7.8 wheels into a clean venv works, and openjd check and openjd run succeed.
    • A fix(model): commit produced a Release PR bumping openjd-model to 0.14.1, which cascaded to openjd-sessions 0.13.2 and openjd-cli 0.7.9. The workflow added the "sync Python package pins" commit, and merging that PR released all three.
    • The first fork run found two problems, both fixed here:
      • The Release PR job raced the release job and proposed re-releasing 0.14.0; it now waits for the release job.
      • A duplicated 0.7.1 entry in the sessions changelog stopped release-plz from parsing it, which left the release notes empty.
  • Not tested: PGP signing and PyPI upload, which need the upstream secrets and trusted publishers. Those steps mirror the old repositories' reusable_release.yml and the PyPA action, but their first run is the real release.

Was this change documented?

  • README has a Python packages section. RELEASING.md describes the Python release flow and one-time setup. AGENTS.md has the package map, CI and release notes.
  • python/openjd-model/AGENTS.md is updated for the workspace layout. The package READMEs link to this repository's releases.

Is this a breaking change?

For openjd-model (Python), yes. Building against the in-repo crates picks up openjd-expr 0.11.0, openjd-model 0.12.0 and openjd-sessions 0.8.0. The PyPI 0.13.0 release used 0.10.1, 0.11.0 and 0.7.3. Hence 0.14.0:

The five affected tests are updated. openjd-sessions and openjd-cli only move their pins.

Two other changes in the imported code:

  • fix(model): mypy failed on mainline with pydantic 2.14 (FieldInfo.annotation is now TypeForm[Any]). This also fails in openjd-model-for-python today.
  • scripts/generate_stubs.sh has its paths updated for the new layout. I couldn't run it end to end: building pyo3-stub-gen 0.23 under abi3-py39 fails on mainline too (the /tmp patch the script clones isn't wired in).

Does this change impact security?

It doesn't change runtime code paths. It changes release automation: PyPI uploads move to this repository's release environment via trusted publishing, and PGP signing uses the same secrets as before. Third-party actions are pinned to commit SHAs (PyO3/maturin-action, pypa/gh-action-pypi-publish, aws-actions/configure-aws-credentials).


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

jusiskin and others added 30 commits October 24, 2025 10:21
…ption/openjd-model-for-python#242)

Signed-off-by: Josh Usiskin <56369778+jusiskin@users.noreply.github.com>
…ption/openjd-sessions-for-python#279)

Signed-off-by: Josh Usiskin <56369778+jusiskin@users.noreply.github.com>
Currently, the `openjd run` and other commands will fail if the job
template they receive has an extension different from .json, .yml, or
.yaml. This change treats all non-.json extensions as YAML, so that job
templates can be named with different extensions like `.ojdt` that
clearly distinguish them from generic JSON or YAML.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
When running in Python 3.14, importing pydantic.v1 produces a warning
message. The code was porting it specifically for a Python 3.9
compatibility issue, so this change is to only do that import in Python
3.9 and for 3.10+ only use the newer pydantic.

Added Python versions 3.13 and 3.14 to the github action test matrix

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
Some of the sample code in the README.md no longer worked after the
change for RFC 0002.
https://github.com/OpenJobDescription/openjd-specifications/blob/mainline/rfcs/0002-model-extensions.md
updated OpenJD to make the fields and validation of template components
depend on an 'extensions' property at the top level of the template.
Implementing this in the model library involved adding a model parsing
context that objects look at during construction and validation.

This change adds a docstring for the parse_model function and updates
the broken examples to explain how the extensions fit in and how to
create model objects.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…eters

Because a job template is runnable code similar to a script, we can
think of `openjd run <templatefile>` the same as `bash <scriptfile>`.
Part of this is printing help output of the options available.

This change makes it so `openjd run <templatefile> -h` prints help that
is specific about the template file, displaying the job name,
description, and all its parameters. Similarly, if there are missing
required parameters, the error message says that and then displays this
same help text.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…ion class (OpenJobDescription/openjd-sessions-for-python#281)

Signed-off-by: Godot Bian <13778003+godobyte@users.noreply.github.com>
…#283)

Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
… to ==10.5.* (OpenJobDescription/openjd-cli#197)

chore(deps): update python-semantic-release requirement

Updates the requirements on [python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) to permit the latest version.
- [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases)
- [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst)
- [Commits](python-semantic-release/python-semantic-release@v10.4...v10.5)

---
updated-dependencies:
- dependency-name: python-semantic-release
  dependency-version: 10.5.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… to ==10.5.* (OpenJobDescription/openjd-model-for-python#246)

chore(deps): update python-semantic-release requirement

Updates the requirements on [python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) to permit the latest version.
- [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases)
- [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst)
- [Commits](python-semantic-release/python-semantic-release@v10.4...v10.5.1)

---
updated-dependencies:
- dependency-name: python-semantic-release
  dependency-version: 10.5.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… to ==10.5.* (OpenJobDescription/openjd-sessions-for-python#284)

chore(deps): update python-semantic-release requirement

Updates the requirements on [python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) to permit the latest version.
- [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases)
- [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst)
- [Commits](python-semantic-release/python-semantic-release@v10.4...v10.5.1)

---
updated-dependencies:
- dependency-name: python-semantic-release
  dependency-version: 10.5.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…openjd-model-for-python#248)

Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…openjd-cli#199)

Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…lder envs (OpenJobDescription/openjd-model-for-python#249)

Signed-off-by: Morgan Epp <60796713+epmog@users.noreply.github.com>
…lder envs (OpenJobDescription/openjd-sessions-for-python#288)

Signed-off-by: Morgan Epp <60796713+epmog@users.noreply.github.com>
…lder envs (OpenJobDescription/openjd-cli#200)

Signed-off-by: Morgan Epp <60796713+epmog@users.noreply.github.com>
…escription/openjd-sessions-for-python#287)

Signed-off-by: Charles Moore <122481442+moorec-aws@users.noreply.github.com>
…escription/openjd-cli#201)

Signed-off-by: Charles Moore <122481442+moorec-aws@users.noreply.github.com>
…escription/openjd-model-for-python#250)

Signed-off-by: Charles Moore <122481442+moorec-aws@users.noreply.github.com>
…openjd-sessions-for-python#286)

Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This new function provides the ability to run an ad hoc command within
the session environment, choosing whether to use the environment
variables from the session enter actions or to ignore them. The
parameters for the command are direct, so you don't have to construct a
temporary Action or StepAction object to run a simple command.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…obDescription/openjd-sessions-for-python#289)

Updates the requirements on [mypy](https://github.com/python/mypy) to permit the latest version.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](python/mypy@v1.18.1...v1.19.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.19.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…obDescription/openjd-cli#202)

Updates the requirements on [mypy](https://github.com/python/mypy) to permit the latest version.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](python/mypy@v1.18.1...v1.19.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.19.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#290)

Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…model-for-python#254)

fix: handle None value for environment field in EnvironmentTemplate

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…obDescription/openjd-model-for-python#251)

Updates the requirements on [mypy](https://github.com/python/mypy) to permit the latest version.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](python/mypy@v1.18.1...v1.19.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.19.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ttblanchard <55503092+ttblanchard@users.noreply.github.com>
leongdl and others added 23 commits September 16, 2026 11:51
…penjd-model-for-python#365)

openjd-expr 0.7.0 -> 0.8.0, openjd-model 0.7.1 -> 0.8.0, and
openjd-sessions 0.5.8 -> 0.6.0 (openjd-rs release PR OpenJobDescription/openjd-model-for-python#395).

Six upstream changes land, reconciled against a source diff of the
published crates. The only new public API is two methods on
openjd_expr::FormatString, segment_count and literal_segments, which
the model crate's validator uses (OpenJobDescription/openjd-model-for-python#383). Both are now exposed on
openjd.expr.FormatString, with the stub and specs/python-expr-interface.md
updated. openjd-model and openjd-sessions have no public signature
changes; the two [breaking] changelog entries are behavioural.

Each change exercised through this package's API, v1 and v0 side by
side where both apply:

* openjd-rs#396 type-checks an `in`/`not in` item against the list's
  element type. `'a' in [1, 2]` now raises where it returned False.
  Int/float and path/string coercion kept; `x in []` is False for any x.
* openjd-rs#383 checks resolved-value constraints at template
  validation. A job name resolving to 129 characters, and a chunks
  defaultTaskCount naming an undeclared parameter, are now rejected at
  decode. v0 already rejected both. The whole-field chunks expression
  is resolved with target type int, so a STRING value fails inside the
  evaluator: test_chunk_default_task_count_invalid_int's message match
  moves from "not a valid integer" to "Cannot convert ... to int"; the
  exception class it pins is unchanged.
* openjd-rs#397: a single-valued allOf with one literal and one
  whole-field expression is now accepted at validation (0.7.1 rejected
  it) because the expression can null-skip, and job creation re-checks
  the resolved count. A control character in a resolved job name is
  rejected at create_job; 0.7.1 produced the job.
* openjd-rs#398 renders NONCONTIGUOUS chunks like the v0 reference:
  [1,2,4,6] is "1,2,4,6", not "1,2-6:2". Both paths agree on all four
  probed shapes.
* openjd-rs#384: preprocess_job_parameters accepts its own output for a
  LIST[PATH] parameter defaulting to []. 0.7.1 refused the second pass.
* openjd-rs#359 rejects embedded filenames ".", ".." and any containing
  a null byte at template validation. v0 accepts all three. The
  sessions-side check for Windows drive anchors ("C:", "D:relative") is
  not reachable from this repo's tests: the model layer accepts them
  and the check is Windows-only.

One test template was invalid and passed only because 0.7.1 did not
validate chunk fields: test_chunks_with_format_string_default_task_count
now declares the parameter it references.

Old-pins mutant: with the three pins reverted and the two new bindings
removed, 30 tests fail, all in the new or updated classes, and the
whole-field allOf deferral was measured as rejected on 0.7.1. Two new
cases (a tab in a literal run of an interpolated name, two certain
allOf elements) were already rejected on 0.7.1 and are labelled
controls.

THIRD-PARTY-LICENSES.txt regenerated; only the three crate versions
changed. Cargo.lock moved only those three packages.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
* chore(release): 0.11.13

Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>

* chore(release): Fill in the 0.11.13 release notes

The generated block was empty: the only commit in the release is a
chore(deps), which the changelog template does not render. Frame it as
the openjd-rs 0.8.0 bump and index the six upstream fixes it carries,
one line each, plus the two new FormatString methods it exposes.

Versions taken from the rust-bindings/Cargo.toml diff in 710ee13; each
line corresponds to a test added in that commit.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>

---------

Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
Co-authored-by: David Leong <116610336+leongdl@users.noreply.github.com>
…n/openjd-model-for-python#367)

* fix: Allow '_' in a combination expression

Template Schemas §3.4.3 constraint 1 gives a <CombinationExpr> the characters
allowed in an <Identifier> plus the space and the operators, and §7.1 puts the
underscore among an <Identifier>'s characters. The character class here omitted
it, so a task parameter named Frame_Range was rejected by the pattern before the
expression parser saw it:

    steps[0] -> parameterSpace -> combination:
        String should match pattern '(?-m:^[A-Za-z0-9\*\(\), ]+\z)'

The name itself was legal, so such a parameter could be declared and never
referenced. Its own comment already claimed the intended relationship to
<Identifier>, so bind both classes to one constant rather than restating it:
the two cannot drift again. The rendered _identifier_regex is unchanged.

The Rust path accepted these all along, so the two lanes disagreed. Add a
control on the v1 side pinning its half of the parity.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>

* fix: Address review on the combination expression class

Two findings from the automated review.

Scope the comment's claim. It said the shared constant meant Identifier and
CombinationExpr "cannot drift", which is stronger than the code provides: only
the character class is shared, not the leading-character rule. Say what is
actually shared, and record why the space is U+0020 alone.

Whitespace stays narrow, deliberately. The shared TokenStream folds every
whitespace run to a space before lexing, so CombinationExpressionParser on its
own accepts a newline that the pattern refuses. §3.4.3 allows "the space", and
openjd-rs refuses a newline too, so the pattern is the conformant side and
widening it to \s would create a divergence rather than close one. Pin it with
a newline case instead of leaving the decision implicit.

Assert the shared constant, not the rendered pattern. Hardcoding the expanded
regex meant a deliberate widening of _identifier_chars would break a test about
hyphens, which is the opposite of what factoring the constant out was for.
Verified: widening the constant now leaves the negative controls green, while
reverting it, widening to any character, and widening the space to \s are each
caught.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>

---------

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
…bDescription/openjd-model-for-python#368)

fix: apply the template's extensions in the job parameter merge

preprocess_job_parameters re-validates each merged job parameter definition
through parse_model, and it passed no supported_extensions. The parsing context
that reached NameIdentifierLengthMixin therefore carried an empty extension set,
so the mixin enforced the base 64-character limit on a parameter name that decode
had already accepted at up to 512 under FEATURE_BUNDLE_1. create_job calls
preprocess_job_parameters itself, so both entry points rejected the name.

Measured before the change, for a template declaring FEATURE_BUNDLE_1 with a
512-character STRING parameter name:

    decode_job_template      OK
    preprocess_job_parameters DecodeValidationError: 1 validation errors for
                              JobStringParameterDefinition name: name must be
                              at most 64 characters long
    create_job                same

merge_job_parameter_definitions now derives the union of the extensions declared
by the job template and any environment templates, and threads it to
merge_job_parameter_definitions_for_one and on into parse_model. The union is
sound because a definition was decoded under its own template's extension set and
every definition of one job parameter carries the same name, so the union cannot
admit a value decode did not already accept for the template that declared it.

The public signature of merge_job_parameter_definitions is unchanged: it already
receives the templates that carry the extensions, so the set is derived rather
than passed. This follows the shape of OpenJobDescription/openjd-model-for-python#362, which fixed the sibling defect in
instantiate_model, and it keeps a caller from declaring an extension the template
did not. merge_job_parameter_definitions_for_one takes a new keyword-only
supported_extensions argument that defaults to None, preserving its behaviour for
any existing caller.

Tests cover all four FEATURE_BUNDLE_1 length ceilings at job-creation time on
both lanes. v0 gains group H in test_feature_bundle_1.py: the parameter name
through the merge, preprocess_job_parameters and create_job for each of STRING,
PATH, INT and FLOAT, a genuine two-source merge, the environment-template-only
case, the EXPR-typed model_copy branch, and controls for the base 64 limit and
the static 512 identifier ceiling. v1 gains an equivalent class in
model_v1/test_create_job.py, which had no coverage of these ceilings at all; the
Rust implementation validates in a single pass and already passes, so those tests
are a ratchet against a re-validation pass being added upstream unnoticed.

Every v0 test was mutation-checked. Seven mutants, each caught: dropping the
threading at any of its three points, hardcoding FEATURE_BUNDLE_1 instead of
deriving it, ignoring environment templates, applying the threading only to
STRING, and applying it only to single-source merges.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
…penjd-model-for-python#369)

openjd-rs released on 2026-09-18: openjd-expr 0.9.0, openjd-model 0.9.0,
openjd-sessions 0.7.0 (OpenJobDescription#401). The release is
one feature plumbed through all three crates -- opt-in resolved-value caps
and evaluation budgets (OpenJobDescription/openjd-model-for-python#399) -- plus a job-creation re-check on
carried-forward fields (OpenJobDescription/openjd-model-for-python#404) and an MSRV/dependency sweep (OpenJobDescription/openjd-model-for-python#403).

Four breaking signature changes reach this package's bindings:
FormatString::validate_expressions now takes a FormatStringOptions,
decode_environment_template takes CallerLimits, evaluate_let_bindings takes
the two budgets, and CallerLimits / SessionConfig gained fields.

CallerLimits grows from six fields to ten, and the four new ones are
threaded to every entry point upstream threads them to: the environment
template decoders, evaluate_let_bindings, and Session, which is the run-time
enforcement boundary for the resolved-value caps.

Separately, max_template_size was inert in this package. It is checked only
inside document_string_to_object, and the parse_string helper passed a
default CallerLimits, so a caller asking for a byte ceiling on
decode_job_template_str got none. parse_string now takes the caller's own
limits. Found while documenting the field.

Cargo.lock moved the three crates plus the OpenJobDescription/openjd-model-for-python#403 sweep; THIRD-PARTY-LICENSES
regenerated to match. Verified: 6139 passed / 24 skipped / 3 xfailed,
coverage 94.16%; ruff, black, mypy, cargo fmt and clippy clean. Six mutants
covering each piece of new plumbing were each caught by the new tests.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
The capability name constraints and the uniqueness of names within
hostRequirements amounts and attributes apply to the names after their
format strings are resolved (OpenJobDescription/openjd-specifications#189).
Template validation only compares the raw names, so two different format
strings that resolved to the same name, or a name that resolved to more
than 100 characters, created a job.

Check the 100-character limit on the resolved amount and attribute names,
and the case-insensitive uniqueness of the resolved names, when the job is
created. The messages match the ones for literal names.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…idation (OpenJobDescription/openjd-model-for-python#372)

The EXPR extension makes job and task parameter type names
case-insensitive (RFC 0007 §2), so `type: string` is exactly as valid as
`type: STRING`. The model accepted such a declaration but then rejected
every reference to the parameter:

    steps[0] -> script -> actions -> onRun -> args[0]:
        Variable Param.Msg does not exist at this location.

The variable-reference prevalidation pass runs on raw template values,
before the field validator that folds the `type` discriminator to upper
case, and it resolved the type-discriminated parameter-definition union
with an exact string comparison. A lowercase or mixed-case type name
matched no member of the union, so the definition contributed none of its
`Param.*` / `RawParam.*` / `Task.Param.*` symbols and none of its EXPR
type information.

Compare the discriminator with an ASCII-only case fold when EXPR is
active, in both the reference-validation and the definition-collection
traversal. Job, task, and environment-template parameters now define
their symbols and types whatever the case of the type name. The fold is
ASCII-only so Unicode look-alikes such as `ıNT` stay rejected, matching
the field validator.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…ption/openjd-model-for-python#373)

* chore(deps): Bump openjd-* Rust crates to the 0.10.0 release

openjd-rs released on 2026-09-28: openjd-expr 0.10.0, openjd-model 0.10.0,
openjd-sessions 0.7.1 (OpenJobDescription#408). This package
pinned 0.9.0 / 0.9.0 / 0.7.0. openjd-sessions 0.7.1 is a transitive re-pin
with no source change.

Two breaking changes, both features:

- OpenJobDescription/openjd-model-for-python#409 makes template::AmountRequirement::name and
  template::AttributeRequirement::name a FormatString instead of a String,
  so a capability name may contain expressions
  (openjd-specifications#189). The §3.3.1.1 / §3.3.2.1 constraints now
  apply to the resolved name.
- OpenJobDescription/openjd-model-for-python#407 requires create_job's context to cover the template's declared
  extensions. With mismatch impossible, the job-creation resolved-value
  checks report every evaluation error instead of silently skipping it.

Only OpenJobDescription/openjd-model-for-python#409 broke compilation, in four places: the two template-type
constructors and the two name getters. The Python-facing `name` stays a
`str` holding the raw template text. v0 models these as
AmountCapabilityName / AttributeCapabilityName, both subclasses of v0's
FormatString, which subclasses str -- so exposing an openjd.expr
.FormatString here would make v0 and v1 diverge where they agree, and
.raw() is what OpenJobDescription/openjd-model-for-python#409 prescribes for reading these fields. One behaviour
change falls out of the adaptation rather than upstream: the constructor
parses, so a malformed format string now raises ExpressionError.

create_job needed no code change. The binding derives its context from
job_template.default_validation_context() when the caller passes none,
which covers the template's extensions by construction; a caller-supplied
mismatched context is what OpenJobDescription/openjd-model-for-python#407 now rejects, and the binding surfaces it.

OpenJobDescription/openjd-model-for-python#407 is a squash of five commits whose message documents three behaviour
changes the release changelog and the PR body do not name:

- eval_boolop no longer suppresses budget errors in operands after an
  unresolved one, the same bypass eval_ifexp had.
- A list comprehension over a *concrete* iterable whose filter evaluates
  unresolved concludes unresolved[list[T]] instead of hard-erroring. This
  was a defect that predated the release, masked by the lenient policy
  OpenJobDescription/openjd-model-for-python#407 deleted, and reachable only at job creation -- the one stage where
  the iterable is concrete and the filter is not.
- Template validation and create_job's re-checks evaluate under
  PathFormat::Posix, so validation outcomes no longer depend on the host
  OS. Windows-only in effect; unverified locally.

Verified: 6243 passed / 24 skipped / 3 xfailed, coverage 94.22%. The three
xfails are the pre-existing openjd.expr known gaps; none flipped. ruff,
black, mypy, cargo fmt and clippy clean. Every behaviour change above was
measured through this package's public API against both 0.9.0 and 0.10.0,
and the openjd.expr expectations are taken from the upstream assertions in
test_unresolved_eval.rs and test_memory.rs.

Four mutants, each rebuilt and each caught: reverting the three pins kills
53 of the new cases, the two __repr__ sites kill 2, the two name getters
kill 11, and swallowing the name parse error kills 2. Each was restored
byte-for-byte with the bytecode cache cleared between runs.

THIRD-PARTY-LICENSES.txt moves only the three version lines; the release
pulled in no new transitive crates.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>

* fix: Expose the capability name as a FormatString, not a str

Review feedback on OpenJobDescription/openjd-model-for-python#373: consistency with the other v1 fields beats
consistency with v0 here.

openjd-rs#409 made template::AmountRequirement::name and
template::AttributeRequirement::name a FormatString. The first pass kept the
Python-facing `name` a `str` and parsed on the way in, on the grounds that
v0 models these as AmountCapabilityName / AttributeCapabilityName, both
subclasses of v0's FormatString, which subclasses str. That argument looks
at the wrong neighbour: every other FormatString-typed field on the v1
template types -- `min`, `max`, `anyOf`, `allOf`, `Action.command` -- is an
openjd.expr.FormatString and refuses a bare str. `name` now matches them.

This deletes more than it adds. parse_capability_name and its doc comment go
away, because the FormatString arrives already parsed, and both constructors
go back to being infallible. Validation moves to where it belongs: a
malformed name is rejected by FormatString's own constructor rather than by
a str-typed field that parses behind the caller's back. __repr__ keeps
.raw(), matching Action's treatment of its command.

This is a breaking change to the v1 Python API, and two pre-existing tests
prove it: TestPickle::test_amount_requirement and test_attribute_requirement
both passed `name=` a str, and TestStepTemplate::test_host_requirements
compared `.name` to one. All three are updated, so this is a behaviour
change and not a refactor.

Also from review: the spec edit that changed the documented ModelProfile repr
to `revision=v2023_09` was wrong -- PyModelProfile::__repr__ still emits the
uppercase-V form (rust-bindings/src/model/profile.rs:397), measured as
`ModelProfile(revision=V2023_09, extensions=[EXPR])`. Reverted. The two
`SpecificationRevision.v2023_09` corrections in the same block stand: that
repr is lowercase (profile.rs:63) and `V2023_09` is not an attribute at all.
The divergence between the two reprs is pre-existing and left alone here.

Verified: 6245 passed / 24 skipped / 3 xfailed, coverage 94.32%. ruff, black,
mypy, cargo fmt and clippy clean. Four mutants, each rebuilt and each caught:
reverting the pins to 0.9.0 kills 68 cases, formatting the FormatString in
both __repr__ sites kills 2, both getters returning a constant kills 12, and
both constructors discarding the supplied name kills 10.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>

---------

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…penJobDescription/openjd-model-for-python#375)

* feat!: Bump openjd-* Rust crates to the openjd-model 0.11.0 release

openjd-rs released openjd-expr 0.10.1, openjd-model 0.10.1 and
openjd-sessions 0.7.2 on 2026-09-30 (OpenJobDescription#416),
then openjd-model 0.11.0 and openjd-sessions 0.7.3 on 2026-10-03
(OpenJobDescription/openjd-model-for-python#420). This package pinned 0.10.0 / 0.10.0 / 0.7.1. Cargo.lock moves
only the three crates; no transitive dependency changed. openjd-sessions
has no source change in either release.

One public Rust API change breaks compilation: OpenJobDescription/openjd-model-for-python#419 makes
template::SimpleAction::script a FormatString. The constructor and the
getter of PySimpleAction were the two errors. Following OpenJobDescription/openjd-model-for-python#373's
capability-name precedent, the Python-facing SimpleAction.script is now
an openjd.expr.FormatString in both directions, like its args/timeout
siblings and the EmbeddedFile.data it desugars to. The new Rust-side
SimpleActionKind / simple_actions / simple_action helpers are not
exposed: nothing here consumes them, and step.bash..node already cover
introspection. resolve_syntax_sugar changed signature but the bindings
never call it.

Behaviour changes, each measured through this package's Python API
against both pins, with expectations copied from upstream assertions:

- OpenJobDescription/openjd-model-for-python#419: bash/python/cmd/powershell/node steps are validated at decode
  with errors at the authored path (steps[0] -> bash -> args[1]). On
  0.10.0, 17 of 21 upstream rejection cases decoded; 9 failed later at
  create_job at the desugared path, 8 were never rejected. A malformed
  script now fails at parse (DecodeValidationError). Non-ASCII step
  names no longer raise PanicException (ASCII-only sanitizer, named in
  the squash body but not the changelog).
- OpenJobDescription/openjd-model-for-python#419, also named only in the squash body: the comprehension-variable
  check now covers a step script's authored embeddedFiles[].data. A
  comprehension over a let-bound name in data decoded on 0.10.0 and is
  now rejected at steps[0] -> script -> embeddedFiles[0] -> data. The
  same check on command and args is unchanged. v0 agrees on outcome and
  path in 4/4 cases.
- OpenJobDescription/openjd-model-for-python#421: LIST[PATH] defaults must be relative and stay inside the
  template directory, are joined to it and normalized, element by
  element; relative submitted PATH / LIST[PATH] values are joined and
  normalized; defaults are constraint-checked after the join, including
  environment-template parameters. Absolute submitted values are
  returned as written. create_job without a preceding preprocess is
  unchanged (measured, 7 cases identical on both pins).
- OpenJobDescription/openjd-model-for-python#410: a job environment's let failure now reads like a step script's
  ("script let binding 'q': ..."); the missing-extension message lists
  extensions sorted.
- OpenJobDescription/openjd-model-for-python#410/OpenJobDescription#417/OpenJobDescription#418: openjd.expr memory accounting; every upstream byte
  figure reproduces. Some expressions that fit now exceed the limit
  (coercion to the target type, string slices, attribute base lookups)
  and some that failed now fit. No existing Python test pinned a moved
  figure.

Unreachable from Python: URI-allowed LIST[PATH] rows (the binding
hard-codes allow_uri_path_values=false, create_job_fns.rs:158; upstream
test_create_job.rs covers them). The Windows rows of OpenJobDescription/openjd-model-for-python#421 are added as
win32-only tests and were not run locally.

v0 divergence: v0 preprocess_job_parameters returns LIST[PATH] defaults
unjoined and does not normalize submitted '..'. openjd-specifications#191
(Template Schemas 2.2, 2.12) makes v1's behaviour normative. Recorded as
two strict xfails in test_known_gaps.py and in the model report; fixing
v0 is a separate change. On the OpenJobDescription/openjd-model-for-python#419 decode cases v0 agrees with v1 on
outcome in 22/24 and on field path in 16/24; the two outcome
differences are v0 rejecting Task.File.<step>_script, which predates
this bump.

Verified: 6323 passed / 26 skipped / 5 xfailed, coverage 94.22%. The +2
skipped are the Windows-only tests, the +2 xfailed the v0 gaps; no
strict xfail flipped. ruff, black, mypy, cargo fmt, build, clippy
-D warnings, test and test --doc clean. THIRD-PARTY-LICENSES.txt
regenerated (three version lines) and verify mode passes.

Mutants, each rebuilt, bytecode cleared, restored by checksum: reverting
the pins and the adaptation kills 62 of 79 new or edited cases (the 17
survivors are controls whose behaviour 0.10.0 shares); the getter
returning str kills 4; the constructor accepting str kills 4.

Not done: the three-auditor review of the new tests
(regression-workflow section 3) was not dispatched; the mutation
results above are the author's own. TestExtremeSliceSteps pins the OpenJobDescription/openjd-model-for-python#418
overflow fix only in a build with overflow checks; a hatch env install
of 0.10.0 already returned the right answer.

BREAKING CHANGE: SimpleAction.script is an openjd.expr.FormatString
instead of str, and SimpleAction(script=...) requires one. LIST[PATH]
defaults must be relative and are joined with the job template
directory; relative submitted PATH values are normalized.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>

* chore(deps): Bump pyo3 to 0.29.3 for clippy 1.99

Rust 1.99.0 (stable since 2026-10-01) makes clippy flag
clone_on_copy inside pyo3 0.29.2's #[pyclass(from_py_object)]
expansion on Copy enums: 13 errors, which fail Rust Quality's
clippy -D warnings step on every PR. pyo3 0.29.3 fixes the
generated code (PyO3/pyo3#6309).

Raise the floor in rust-bindings/Cargo.toml to 0.29.3 so a fresh
resolve cannot pick the affected release. THIRD-PARTY-LICENSES.txt
regenerated; only the four pyo3 version lines changed.

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>

---------

Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
…for-python#368)

Update to the latest openjd-model release.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
…#369)

Signed-off-by: client-software-ci <129794699+client-software-ci@users.noreply.github.com>
Imported from https://github.com/OpenJobDescription/openjd-model-for-python (mainline) with
git filter-repo. Paths are moved under python/openjd-model/, tags are renamed
to legacy/openjd-model-for-python/<version>, and PR references are rewritten to
OpenJobDescription/openjd-model-for-python#<n>.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…essions

Imported from https://github.com/OpenJobDescription/openjd-sessions-for-python (mainline) with
git filter-repo. Paths are moved under python/openjd-sessions/, tags are renamed
to legacy/openjd-sessions-for-python/<version>, and PR references are rewritten to
OpenJobDescription/openjd-sessions-for-python#<n>.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Imported from https://github.com/OpenJobDescription/openjd-cli (mainline) with
git filter-repo. Paths are moved under python/openjd-cli/, tags are renamed
to legacy/openjd-cli/<version>, and PR references are rewritten to
OpenJobDescription/openjd-cli#<n>.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Make python/openjd-model, python/openjd-sessions and python/openjd-cli part
of the openjd-rs workspace so a change to the Rust crates and the Python
packages that expose them lands, is tested, and is released together.

- openjd-model's PyO3 bindings crate (renamed openjd-model-py) moves its
  manifest to the Python package root and depends on openjd-expr,
  openjd-model and openjd-sessions by path instead of from crates.io. The
  nested Cargo workspace, Cargo.lock and deny.toml are removed in favor of
  the root ones.
- openjd-sessions and openjd-cli get an empty `publish = false` release
  anchor crate whose version is the Python package version.
- Python versions now come from each package's Cargo.toml (maturin reads it
  natively; hatch reads it with a regex) instead of from git tags. This
  removes the in-tree PEP 517 backend, generate_version.py,
  maturin_build.py and the hatch version-copy hooks. _version.py is now a
  committed file that reads the installed distribution's metadata.
- sessions and cli hatch environments install the in-repo openjd-model
  (and openjd-sessions) before themselves, so their tests run against the
  code in this repo rather than the PyPI releases. openjd-cli's pins move
  to the in-repo 0.13 versions, which its test suite passes against.
- Per-repo GitHub configuration, semantic-release configuration and
  duplicated CODE_OF_CONDUCT/CONTRIBUTING files are removed; the
  repo-level ones apply.

Building against the in-repo crates picks up behavior changes from
openjd-expr 0.11 and openjd-model 0.12 that Python users can see. The
affected tests are updated:

- Syntax errors use CPython's wording, e.g. "'(' was never closed" instead
  of "unexpected EOF while parsing" (ruff parser 0.16.10, openjd-rs#429).
- A failing `let` binding in create_job raises ModelValidationError at the
  binding's template path instead of ExpressionError (openjd-rs#428).

BREAKING CHANGE: openjd-model's create_job raises ModelValidationError,
not ExpressionError, when a `let` binding fails to evaluate, and reports
every job-creation error at once.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
pydantic 2.14 annotates FieldInfo.annotation as TypeForm[Any], which mypy
rejects where the variable-reference validators expect type[Any]. Cast to
the runtime type the annotation holds. This also fails on
openjd-model-for-python's mainline with current dependencies.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Add a Python workflow that lints, builds, and tests openjd-model,
openjd-sessions and openjd-cli on Linux, Windows and macOS across the
Python versions each package supported in its own repository, each
against the in-repo versions of the others. It also runs openjd-sessions'
macOS cross-user tests, checks openjd-model's THIRD-PARTY-LICENSES.txt,
and checks the pins between the Python packages with the new
scripts/sync_python_pins.py.

CodeQL now analyzes Python as well as Rust, and Dependabot updates the
Python packages' pip requirements.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
release-plz now versions openjd-model, openjd-sessions and openjd-cli
alongside the crates, so one Release PR covers every package that changed
and merging it releases all of them.

- release-plz.toml: the three Python packages are `git_only` (their last
  release comes from a python-<distribution>-v<version> tag, not
  crates.io) and are never published to crates.io.
- release-plz.yml: when `release` creates a Python tag, build the
  openjd-model wheels (six platforms, abi3) and sdist and the
  openjd-sessions/openjd-cli wheel and sdist from that tag, PGP-sign them
  as the per-package repositories did, attach them to the GitHub Release,
  and upload them to PyPI in dependency order.
- On the Release PR, run scripts/sync_python_pins.py after release-plz so
  the pyproject.toml pins between the Python packages follow the version
  bumps (the Cargo dependencies between them cascade the bumps).
- Python releases no longer get the Rust THIRD-PARTY-LICENSES attachment;
  openjd-model ships its own.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Describe the python/ packages in the README, AGENTS.md and RELEASING.md
(including the one-time PyPI trusted-publishing and signing setup), and
update python/openjd-model's AGENTS.md, generate_stubs.sh and the package
READMEs for the workspace layout and the release-plz flow.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…li 0.7.8

Set the first versions released from this repository and write their
changelog entries. With no python-<distribution>-v<version> tag for these
versions yet, the release-plz `release` job on the merge commit tags and
publishes them; later Release PRs start from those tags.

- openjd-model 0.14.0: built from the in-repo openjd-expr 0.11.0,
  openjd-model 0.12.0 and openjd-sessions 0.8.0, a breaking change for
  create_job's let-binding errors.
- openjd-sessions 0.13.1 and openjd-cli 0.7.8: require the new versions
  (pins updated with scripts/sync_python_pins.py).

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

echo "Creating ephemeral venv..."
python -m venv "$venv_dir"
"$venv_dir/bin/pip" install --quiet --upgrade pip >/dev/null
"$venv_dir/bin/pip" install --quiet "$wheel_dir"/openjd_model-*.whl pip-licenses >/dev/null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should fix: The Python half of this check depends on whatever PyPI serves at run time, not on anything in the repo. The step installs the wheel with pydantic >= 2.10, < 3 and pyyaml == 6.0.* unpinned, and pip-licenses writes each resolved version into the output (** name; version X).

Failure: pydantic, pydantic-core, typing-extensions, annotated-types or typing-inspection ships a new release. The regenerated file then no longer matches the committed THIRD-PARTY-LICENSES.txt, so the Python package checks job in python.yml fails on every PR that touches python/ or the expr/model/sessions crates, even when the PR doesn't change dependencies.

Fix: resolve the Python section from a pinned constraints/lock file that gets updated together with the committed file. Or compare only package names and licenses, not versions.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Claude review · advisory

Reviewed ffbc1c5 (full review): 1 finding.

Open: 0 blocking · 1 should-fix · 0 nit. ❌ Address or reply to the open threads.

Fix or reply to each thread; the next revision's review re-checks open threads and resolves those it agrees are handled. Resolving a thread yourself also closes it. Later revisions review only what changed.

@crowecawcaw

Copy link
Copy Markdown
Contributor Author

Useful to see we can do it. Not for right now.

@crowecawcaw crowecawcaw closed this Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.