All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Removed dependence on Sentinel module
- Tests across multiple versions of Python and multiple operating systems
- Documentation reflecting supported configurations
- Typing: Use
bool
in place ofOptional[bool]
as type annotation fordevelop
parameter incovalent_dispatcher.service._graceful_start
- Typing: Use
Any
in place ofOptional[Any]
as type annotation fornew_value
parameter incovalent._shared_files.config.get_config
- Updated hyperlink of "How to get the results" from "./collection/query_electron_execution_result" to "./collection/query_multiple_lattice_execution_results" in "doc/source/how_to/index.rst".
- Updated hyperlink of "How to get the result of a particular electron" from "./collection/query_multiple_lattice_execution_results" to "./collection/query_electron_execution_result" in "doc/source/how_to/index.rst".
- Changelog entries are now required to have the current date to enforce ordering.
- UI: log file output - display in Output tab of all available log file output
- UI: show lattice and electron inputs
- UI: display executor attributes
- UI: display error message on failed status for lattice and electron
- UI: re-order sidebar sections according to latest figma designs
- UI: update favicon
- UI: remove dispatch id from tab title
- UI: fit new uuids
- UI: adjust theme text primary and secondary colors
- UI: auto-refresh result state on initial render of listing and graph pages
- UI: graph layout issues: truncate long electron/param names
- Added
BaseDispatcher
class to be used for creating custom dispatchers which allow connection to a dispatcher server. LocalDispatcher
inheriting fromBaseDispatcher
allows connection to a local dispatcher server running on the user's machine.- Covalent only gives interface to the
LocalDispatcher
'sdispatch
anddispatch_sync
methods. - Tests for both
LocalDispatcher
andBaseDispatcher
added.
- Switched from using
lattice.dispatch
andlattice.dispatch_sync
tocovalent.dispatch
andcovalent.dispatch_sync
. - Dispatcher address now is passed as a parameter (
dispatcher_addr
) tocovalent.dispatch
andcovalent.dispatch_sync
instead of a metadata field to lattice. - Updated tests, how tos, and tutorials to use
covalent.dispatch
andcovalent.dispatch_sync
. - All the contents of
covalent_dispatcher/_core/__init__.py
are moved tocovalent_dispatcher/_core/execution.py
for better organization.__init__.py
only contains function imports which are needed by external modules. dispatch
,dispatch_sync
methods deprecated fromLattice
.
_server_dispatch
method removed fromLattice
.dispatcher
metadata field removed fromlattice
.
_write_dispatch_to_python_file
isn't called each time a task is saved. It is now only called in the final save in_run_planned_workflow
(in covalent_dispatcher/_core/init.py).
- Added type information to result.py
- Replaced
"typing.Optional"
with"str"
in covalent/executor/base.py - Added missing type hints to
get_dispatch_context
andwrite_streams_to_file
in covalent/executor/base.py, BaseExecutor
- Functions to check if UI and dispatcher servers are running.
- Tests for the
is_ui_running
andis_server_running
in covalent_dispatcher/_cli/service.py.
- Covalent CLI command
covalent purge
will now stop the servers before deleting all the pid files.
- Test for
purge
method in covalent_dispatcher/_cli/service.py.
- Unused
covalent_dispatcher
import from covalent_dispatcher/_cli/service.py.
- Moved
_config_manager
import from within thepurge
method to the covalent_dispatcher/_cli/service.py for the purpose of mocking in tests.
- Type hint to
_server_dispatch
method incovalent/_workflow/lattice.py
.
- When the local executor's
log_stdout
andlog_stderr
config variables are relative paths, they should go inside the results directory. Previously that was queried from the config, but now it's queried from the lattice metadata.
- Tests for the corresponding functions in (
covalent_dispatcher/_core/__init__.py
,covalent/executor/base.py
,covalent/executor/executor_plugins/local.py
andcovalent/executor/__init__.py
) affected by the bug fix.
- Refactored
_delete_result
in result manager to give the option of deleting the result parent directory.
- Diff check in pypi.yml ensures correct files are packaged
- Removed codecov token
- Removed Slack notifications from feature branches
- Running tests, conda, and version workflows on pull requests, not just pushes
- Fixing version check action so that it doesn't run on commits that are in develop
- Edited PR template so that markdown checklist appears properly
- publish workflow, using
docker buildx
to build images for x86 and ARM, prepare manifest and push to ECR so that pulls will match the correct architecture. - typo in CONTRIBUTING
- installing
gcc
in Docker image so Docker can build wheels fordask
and other packages that don't provide ARM wheels
- updated versions in
requirements.txt
formatplotlib
anddask
MANIFEST.in
did not havecovalent_dispatcher/_service
in it due to which the PyPi package was not being built correctly. Added thecovalent_dispatcher/_service
to theMANIFEST.in
file.
- setuptools properly including data files during installation
- Added service folder in covalent dispatcher to package.
README.md
images now use master branch's raw image urls hosted on https://github.com instead of https://raw.githubusercontent.com. Also, switched image rendering from html to markdown.
- dispatcher server app included in sdist
- raw image urls properly used
- raw image urls used in readme
- pypi upload
- Code of conduct
- Manifest.in file
- Citation info
- Action to upload to pypi
- Absolute URLs used in README
- Workflow badges updated URLs
install_package_data
->include_package_data
insetup.py
- Using public ECR for Docker release
- GitHub pull request templates
- GitHub issue templates
- Covalent Beta Release
- iframe in the docs landing page is now responsive
- Temporarily removed output tab
- Truncated dispatch id to fit left sidebar, add tooltip to show full id
- Many stylistic improvements to documentation, README, and CONTRIBUTING.
- Test added to check whether an already decorated function works as expected with Covalent.
pennylane
package added to therequirements-dev.txt
file.
- Now using
inspect.signature
instead offunction.__code__
to get the names of function's parameters.
- Various CI fixes, including rolling back regression in version validation, caching on s3 hosted badges, applying releases and tags correctly.
- Removed comments and unused functions in covalent_dispatcher
result_class.py
renamed toresult.py
- Version was not being properly imported inside
covalent/__init__.py
dispatch_sync
was not previously using theresults_dir
metadata field
- Credentials in config
generate_random_filename_in_cache
is_any_atom
to_json
show_subgraph
option indraw
calculate_node
- The gunicorn servers now restart more gracefully
tempdir
metadata field removed and replaced withexecutor.local.cache_dir
- Concepts page
Result.CANCELLED
status to represent the status of a cancelled dispatch.- Condition to cancel the whole dispatch if any of the nodes are cancelled.
cancel_workflow
function which uses a shared variable provided by Dask (dask.distributed.Variable
) in a dask client to inform nodes to stop execution.- Cancel function for dispatcher server API which will allow the server to terminate the dispatch.
- How to notebook for cancelling a dispatched job.
- Test to verify whether cancellation of dispatched jobs is working as expected.
cancel
function is available ascovalent.cancel
.
- In file
covalent/_shared_files/config.py
instead of using a variable to store and then return the config data, now directly returning the configuration. - Using
fire_and_forget
to dispatch a job instead of a dictionary of Dask'sFuture
objects so that we won't have to manage the lifecycle of those futures. - The
test_run_dispatcher
test was changed to reflect that the dispatcher no longer uses a dictionary of future objects as it was not being utilized anywhere.
with dask_client
context was removed as the client created incovalent_dispatcher/_core/__init__.py
is already being used even without the context. Furthermore, it creates issues when that context is exited which is unnecessary at the first place hence not needed to be resolved.
- Results directory uses a relative path by default and can be overridden by the environment variable
COVALENT_RESULTS_DIR
.
- Executor parameters use defaults specified in config TOML
- If relative paths are supplied for stdout and stderr, those files are created inside the results directory
- Sync function
- Covalent CLI tool can restart in developer mode
- Updated the UI address referenced in the README
- Quantum gravity tutorial
- Moved VERSION file to top level
error
attribute was added to the results object to show which node failed and the reason behind it.stdout
andstderr
attributes were added to a node's result to store any stdout and stderr printing done inside an electron/node.- Test to verify whether
stdout
andstderr
are being stored in the result object.
- Redesign of how
redirect_stdout
andredirect_stderr
contexts in executor now work to allow storing their respective outputs. - Executors now also return
stdout
andstderr
strings, along with the execution output, so that they can be stored in their result object.
- Added an attribute
__code__
to electron and lattice which is a copy of their respective function's__code__
attribute. - Positional arguments,
args
, are now merged with keyword arguments,kwargs
, as close as possible to where they are passed. This was done to make sure we support both with minimal changes and without losing the name of variables passed. - Tests to ensure usage of positional arguments works as intended.
- Slight rework to how any print statements in lattice are sent to null.
- Changed
test_dispatcher_functional
inbasic_dispatcher_test.py
to account for the support ofargs
and removed a an unnecessaryprint
statement.
- Removed
args
from electron'sinit
as it wasn't being used anywhere.
- Requirement changed from
dask[complete]
todask[distributed]
.
- New UI static demo build
- New UI toolbar functions - orientation, toggle params, minimap
- Sortable and searchable lattice name row
- Numerous UI style tweaks, mostly around dispatches table states
- Node sidebar info now updates correctly
- Unused numpy requirement. Note that numpy is still being installed indirectly as other packages in the requirements rely on it.
- How-to guide for Covalent dispatcher CLI.
- Switched from using human readable ids to using UUIDs
human-id
package was removed along with its mention inrequirements.txt
andmeta.yaml
- Code breaking text from CLI api documentation.
- Unwanted covalent_dispatcher rst file.
- Installation of entire covalent_dispatcher instead of covalent_dispatcher/_service in setup.py.
- Functions with multi-line or really long decorators are properly serialized in dispatch_source.py.
- Multi-line Covalent output is properly commented out in dispatch_source.py.
- Sub-lattice functions are successfully serialized in the utils.py get_serialized_function_str.
- Function to scan utilized source files and return a set of imported modules (utils.get_imports_from_source)
- UI runs on port 47007 and the dispatcher runs on port 48008. This is so that when the servers are later merged, users continue using port 47007 in the browser.
- Small modifications to the documentation
- Small fix to the README
- Removed a directory
generated
which was improperly added - Dispatcher web interface
- sqlalchemy requirement
- In file
covalent/executor/base.py
,pickle
was changed tocloudpickle
because of its universal pickling ability.
- In docstring of
BaseExecutor
, a note was added specifying thatcovalent
with its dependencies is assumed to be installed in the conda environments. - Above note was also added to the conda env selector how-to.
- Replaced the generic
RuntimeError
telling users to check if there is an object manipulation taking place inside the lattice to a simple warning. This makes the original error more visible.
- If condition added for handling the case where
__getattr__
of an electron is accessed to detect magic functions.
ActiveLatticeManager
now subclasses fromthreading.local
to make it thread-safe.ValueError
in the lattice manager'sclaim
function now also shows the name of the lattice that is currently claimed.- Changed docstring of
ActiveLatticeManager
to note that now it is thread-safe. - Sublattice dispatching now no longer deletes the result object file and is dispatched normally instead of in a serverless manner.
simulate_nitrogen_and_copper_slab_interaction.ipynb
notebook tutorial now does normal dispatching as well instead of serverless dispatching. Also, now 7 datapoints will be shown instead of 10 earlier.
- Passing AWS credentials to reusable workflows as a secret
- Action to push development image to ECR
- Made the publish action reusable and callable
- Updated the README
- Updated classifiers in the setup.py file
- Massaged some RTD pages
- Action to push static UI to S3
- Completed new UI design work
- Added eventlet requirement
- The CLI tool can now manage the UI flask server as well
- [Breaking] The CLI option
-t
has been changed to-d
, which starts the servers in developer mode and exposes unit tests to the server.
- Config manager in
covalent/_shared_files/config.py
- Default location for the main config file can be overridden using the environment variable
COVALENT_CONFIG_DIR
- Ability to set and get configuration using
get_config
andset_config
- The flask servers now reference the config file
- Defaults reference the config file
ValueError
caught when runningcovalent stop
- One of the functional tests was using a malformed path
- The
electron.to_json
function - The
generate_random_filename_in_cache
function
- The
get_api_token
function
- Tutorial section headings
- Plot background white color
- Having a print statement inside electron and lattice code no longer causes the workflow to fail.
- Completed UI feature set for first release
- UI server result serialization improvements
- UI result update webhook no longer fails on request exceptions, logs warning intead
- Astrophysics tutorial
-
Added
get_all_node_results
method inresult_class.py
to return result of all node executions. -
Added
test_parallelilization
test to verify whether the execution is now being achieved in parallel.
-
Removed
LocalCluster
cluster creation usage to a simpleClient
one from Dask. -
Removed unnecessary
to_run
function as we no longer needed to run execution through an asyncio loop. -
Removed
async
from function definition of previously asynchronous functions,_run_task
,_run_planned_workflow
,_plan_workflow
, and_run_workflow
. -
Removed
uvloop
from requirements. -
Renamed
test_get_results
totest_get_result
. -
Reran the how to notebooks where execution time was mentioned.
-
Changed how
dispatch_info
context manager was working to account for multiple nodes accessing it at the same time.
- Changed the software license to GNU Affero 3.0
covalent-ui
directory
- Gunicorn logging now uses the
capture-output
flag instead of redirecting stdout and stderr
- Cleaned up the requirements and moved developer requirements to a separate file inside
tests
- Conda build CI job
- Gunicorn server now checks for port availability before starting
- The
covalent start
function now prints the correct port if the server is already running.
- Covalent tutorial comparing quantum support vector machines with support vector machine algorithms implemented in qiskit and scikit-learn.
- Now using
--daemon
in gunicorn to start the server, which was the original intention.
- Removed finance references from docs
- Fixed some other small errors
- Removed one of the failing how-to tests from the functional test suite
- Web UI prototype
- CLI command
covalent status
shows port information
- gunicorn management improved
- Slack notifications for test status
- Specifying a non-default results directory in a sub-lattice no longer causes a failure in lattice execution.
- Functional tests for how-to's in documentation
- Moved example script to a functional test in the pipeline
- Added a test flag to the CLI tool
- Check that only
kwargs
without any default values in the workflow definition need to be passed inlattice.draw(ax=ax, **kwargs)
.
- Function to check whether all the parameters without default values for a callable function has been passed added to shared utils.
- Content and style fixes for getting started doc.
- Remove all imports from the
covalent
to thecovalent_dispatcher
, except for_dispatch_serverless
- Moved CLI into
covalent_dispatcher
- Moved executors to
covalent
directory
- Updated CONTRIBUTING to clarify docstring style.
- Fixed docstrings for
calculate_node
andcheck_constraint_specific_sum
.
-
prefix_separator
for separating non-executable node types from executable ones. -
subscript_prefix
,generator_prefix
,sublattice_prefix
,attr_prefix
for prefixes of subscripts, generators, sublattices, and attributes, when called on an electron and added to the transport graph. -
exclude_from_postprocess
list of prefixes to denote those nodes which won't be used in post processing the workflow. -
__int__()
,__float__()
,__complex__()
for converting a node to an integer, float, or complex to a value of 0 then handling those types in post processing. -
__iter__()
generator added to Electron for supporting multiple return values from an electron execution. -
__getattr__()
added to Electron for supporting attribute access on the node output. -
__getitem__()
added to Electron for supporting subscripting on the node output. -
electron_outputs
added as an attribute to lattice.
-
electron_list_prefix
,electron_dict_prefix
,parameter_prefix
modified to reflect new way to assign prefixes to nodes. -
In
build_graph
instead of ignoring all exceptions, now the exception is shown alongwith the runtime error notifying that object manipulation should be avoided inside a lattice. -
node_id
changed toself.node_id
in Electron's__call__()
. -
parameter
type electrons now have the default metadata instead of empty dictionary. -
Instead of deserializing and checking whether a sublattice is there, now a
sublattice_prefix
is used to denote when a node is a sublattice. -
In
dispatcher_stack_test
,test_dispatcher_flow
updated to indicate the new use ofparameter_prefix
.
- When an execution fails due to something happening in
run_workflow
, then result object's status is now failed and the object is saved alongwith throwing the appropriate exception.
- Added tests for choosing specific executors inside electron initialization.
- Added test for choosing specific Conda environments inside electron initialization.
- Removed _shared_files directory and contents from covalent_dispatcher. Logging in covalent_dispatcher now uses the logger in covalent/_shared_files/logging.py.
- Decorator symbols were added to the pseudo-code in the quantum chemistry tutorial.
- Quantum chemistry tutorial.
- Docstrings with typehints for covalent dispatcher functions added.
-
Replaced
node
tonode_id
inelectron.py
. -
Removed unnecessary
enumerate
incovalent_dispatcher/_core/__init__.py
. -
Removed
get_node_device_mapping
function fromcovalent_dispatcher/_core/__init__.py
and moved the definition to directly add the mapping toworkflow_schedule
. -
Replaced iterable length comparison for
executor_specific_exec_cmds
fromif len(executor_specific_exec_cmds) > 0
toif executor_specific_exec_cmds
.
- Executors can now accept the name of a Conda environment. If that environment exists, the operations of any electron using that executor are performed in that Conda environment.
- How to estimate lattice execution time has been renamed to How to query lattice execution time.
- Change result querying syntax in how-to guides from
lattice.get_result
tocovalent.get_result
. - Choose random port for Dask dashboard address by setting
dashboard_address
to ':0' inLocalCluster
.
- "Default" executor plugins are included as part of the package upon install.
- Upgraded dask to 2021.10.0 based on a vulnerability report
- Transportable object tests
- Transport graph tests
- Variable name node_num to node_id
- Variable name node_idx to node_id
- Transport graph
get_dependencies()
method return type was changed from Dict to List
- Date handling in changelog validation
- GitLab CI YAML
-
A new parameter to a node's result called
sublattice_result
is added. This will be of aResult
type and will contain the result of that sublattice's execution. If a normal electron is executed, this will beNone
. -
In
_delete_result
function inresults_manager.py
, an empty results directory will now be deleted. -
Name of a sublattice node will also contain
(sublattice)
. -
Added
_dispatch_sync_serverless
which synchronously dispatches without a server and waits for a result to be returned. This is the method used to dispatch a sublattice. -
Test for sublatticing is added.
-
How-to guide added for sublatticing explaining the new features.
-
Partially changed
draw
function inlattice.py
to also draw the subgraph of the sublattice when drawing the main graph of the lattice. The change is incomplete as we intend to add this feature later. -
Instead of returning
plt
,draw
now returns theax
object. -
__call__
function inlattice.py
now runs the lattice's function normally instead of dispatching it. -
_run_task
function now checks whether current node is a sublattice and acts accordingly.
-
Unnecessary lines to rename the node's name in
covalent_dispatcher/_core/__init__.py
are removed. -
test_electron_takes_nested_iterables
test was being ignored due to a spelling mistake. Fixed and modified to follow the new pattern.
- Electrons can now accept an executor object using the "backend" keyword argument. "backend" can still take a string naming the executor module.
- Electrons and lattices no longer have Slurm metadata associated with the executor, as that information should be contained in the executor object being used as an input argument.
- The "backend" keyword can still be a string specifying the executor module, but only if the executor doesn't need any metadata.
- Executor plugin classes are now directly available to covalent, eg: covalent.executor.LocalExecutor().
- Docstrings without examples for all the functions in core covalent.
- Typehints in those functions as well.
- Used
typing.TYPE_CHECKING
to prevent cyclic imports when writing typehints.
convert_to_lattice_function
renamed toconvert_to_lattice_function_call
.- Context managers now raise a
ValueError
instead of a genericException
.
- Fixed the version used in the documentation
- Fixed the badge URLs to prevent caching
- Broken how-to links
- Redundant lines from .gitignore
- *.ipynb from .gitignore
- How-to guides for workflow orchestration.
- How to construct an electron
- How to construct a lattice
- How to add an electron to lattice
- How to visualize the lattice
- How to add constraints to lattices
- How-to guides for workflow and subtask execution.
- How to execute individual electrons
- How to execute a lattice
- How to execute multiple lattices
- How-to guides for status querying.
- How to query electron execution status
- How to query lattice execution status
- How to query lattice execution time
- How-to guides for results collection
- How to query electron execution results
- How to query lattice execution results
- How to query multiple lattice execution results
- Str method for the results object.
- Saving the electron execution status when the subtask is running.
- JWT token requirement.
- Covalent dispatcher login requirement.
- Update covalent login reference in README.md.
- Changed the default dispatcher server port from 5000 to 47007.
- Github action for tests and coverage
- Badges for tests and coverage
- If tests pass then develop is pushed to master
- Add release action which tags and creates a release for minor version upgrades
- Add badges action which runs linter, and upload badges for version, linter score, and platform
- Add publish action (and badge) which builds a Docker image and uploads it to the AWS ECR
- Github action which checks version increment and changelog entry
- New Covalent RTD theme
- sphinx extension sphinx-click for CLI RTD
- Sections in RTD
- init.py in both covalent-dispatcher logger module and cli module for it to be importable in sphinx
- docutils version that was conflicting with sphinx
- Old aq-theme
- Integration tests combining both covalent and covalent-dispatcher modules to test that lattice workflow are properly planned and executed.
- Integration tests for the covalent-dispatcher init module.
- pytest-asyncio added to requirements.
- Results manager file to get results from a file, delete a result, and redispatch a result object.
- Results can also be awaited to only return a result if it has either been completed or failed.
- Results class which is used to store the results with all the information needed to be used again along with saving the results to a file functionality.
- A result object will be a mercurial object which will be updated by the dispatcher and saved to a file throughout the dispatching and execution parts.
- Direct manipulation of the transport graph inside a result object takes place.
- Utility to convert a function definition string to a function and vice-versa.
- Status class to denote the status of a result object and of each node execution in the transport graph.
- Start and end times are now also stored for each node execution as well as for the whole dispatch.
- Logging of
stdout
andstderr
can be done by passing in thelog_stdout
,log_stderr
named metadata respectively while dispatching. - In order to get the result of a certain dispatch, the
dispatch_id
, theresults_dir
, and thewait
parameter can be passed in. If everything is default, then only the dispatch id is required, waiting will not be done, and the result directory will be in the current working directory with folder name asresults/
inside which every new dispatch will have a new folder named according to their respective dispatch ids, containing:result.pkl
- (Cloud)pickled result object.result_info.yaml
- yaml file with high level information about the result and its execution.dispatch_source.py
- python file generated, containing the original function definitions of lattice and electrons which can be used to dispatch again.
logfile
named metadata is nowslurm_logfile
.- Instead of using
jsonpickle
,cloudpickle
is being used everywhere to maintain consistency. to_json
function usesjson
instead ofjsonpickle
now in electron and lattice definitions.post_processing
moved to the dispatcher, so the dispatcher will now store a finished execution result in the results folder as specified by the user with no requirement of post processing it from the client/user side.run_task
function in dispatcher modified to check if a node has completed execution and return it if it has, else continue its execution. This also takes care of cases if the server has been closed mid execution, then it can be started again from the last saved state, and the user won't have to wait for the whole execution.- Instead of passing in the transport graph and dispatch id everywhere, the result object is being passed around, except for the
asyncio
part where the dispatch id and results directory is being passed which afterwards lets the core dispatcher know where to get the result object from and operate on it. - Getting result of parent node executions of the graph, is now being done using the result object's graph. Storing of each execution's result is also done there.
- Tests updated to reflect the changes made. They are also being run in a serverless manner.
LatticeResult
class removed.jsonpickle
requirement removed.WorkflowExecutionResult
,TaskExecutionResult
, andExecutionError
singleton classes removed.
- Commented out the
jwt_required()
part incovalent-dispatcher/_service/app.py
, may be removed in later iterations. - Dispatcher server will now return the error message in the response of getting result if it fails instead of sending every result ever as a response.
- Added a note in Known Issues regarding port conflict warning.
- Added badges to README.md
- Removed old coverage badge and fixed the badge URL
- Codecov integrations and badge
- Detached pipelines no longer created
- Wrote a Code of Conduct based on https://www.contributor-covenant.org/
- Added installation and environment setup details in CONTRIBUTING
- Added Known Issues section to README
- Removed non-open-source executors from Covalent. The local SLURM executor is now
- a separate repo. Executors are now plugins.
- Pythonic CLI tool. Install the package and run
covalent --help
for a usage description. - Login and logout functionality.
- Executor registration/deregistration skeleton code.
- Dispatcher service start, stop, status, and restart.
- JWT token is stored to file instead of in an environment variable.
- The Dask client attempts to connect to an existing server.
- Removed the Bash CLI tool.
- Version assignment in the covalent init file.
- Fixed the Dockerfile so that it runs the dispatcher server from the covalent repo.
- Single line change in ci script so that it doesn't exit after validating the version.
- Using
rules
inpytest
so that the behavior in test stage is consistent.
- CHANGELOG.md to track changes (this file).
- Semantic versioning in VERSION.
- CI pipeline job to enforce versioning.