Repository navigation
Conversation
Contributor
There was a problem hiding this comment.
2 issues found across 30 files
Confidence score: 2/5
spec.mdbrings Activities query/display performance back into this feature, even though that work moved to INFP-727 and retention must wait for IFC-2933. Align the spec with those dependencies.plan.mdallows any caller who can reach the task-manager API to trigger cleanup and request the optional rewrite without authentication. Add authentication or obtain an approved security exception.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="dev/specs/infp-507-task-history-retention/spec.md">
<violation number="1" location="dev/specs/infp-507-task-history-retention/spec.md:22">
P2: According to linked Jira issue INFP-507, Activities query/display performance moved to INFP-727, and retention must not ship ahead of IFC-2933. This sentence brings that work back into this feature; align the spec's scope and delivery order with the current decision.</violation>
</file>
<file name="dev/specs/infp-507-task-history-retention/plan.md">
<violation number="1" location="dev/specs/infp-507-task-history-retention/plan.md:49">
P2: Do not ship this unauthenticated cleanup mutation without an approved security exception: any caller that can reach the task-manager API can trigger cleanup and request the optional rewrite. Add authentication or obtain and record maintainer approval with an enforced network boundary.</violation>
</file>
Shadow auto-approve: would not auto-approve because issues were found.
Re-trigger cubic
|
|
||
| Product decided that each instance keeps both kinds of records for a configured period, deletes older records automatically without affecting the instance, keeps the activity log for its own retention whatever the task manager keeps of its own records, and gets the disk space back. A year or more of activity log is supported in every edition, built so that long retention can become an Enterprise feature later. | ||
|
|
||
| The Activities page gets slower as the activity log grows: with a year of activity log, one page takes minutes today. The Activities page work therefore ships with this feature, ahead of the activity log retention. |
Contributor
There was a problem hiding this comment.
P2: According to linked Jira issue INFP-507, Activities query/display performance moved to INFP-727, and retention must not ship ahead of IFC-2933. This sentence brings that work back into this feature; align the spec's scope and delivery order with the current decision.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At dev/specs/infp-507-task-history-retention/spec.md, line 22:
<comment>According to linked Jira issue INFP-507, Activities query/display performance moved to INFP-727, and retention must not ship ahead of IFC-2933. This sentence brings that work back into this feature; align the spec's scope and delivery order with the current decision.</comment>
<file context>
@@ -0,0 +1,242 @@
+
+Product decided that each instance keeps both kinds of records for a configured period, deletes older records automatically without affecting the instance, keeps the activity log for its own retention whatever the task manager keeps of its own records, and gets the disk space back. A year or more of activity log is supported in every edition, built so that long retention can become an Enterprise feature later.
+
+The Activities page gets slower as the activity log grows: with a year of activity log, one page takes minutes today. The Activities page work therefore ships with this feature, ahead of the activity log retention.
+
+## User Scenarios & Testing *(mandatory)*
</file context>
2 of 5 tasks
This was referenced Oct 4, 2026
saltas888
force-pushed
the
task-history-retention-infp-507
branch
from
October 11, 2026 18:00
35b8e56 to
b9db2c4
Compare
saltas888
commented
Oct 14, 2026
saltas888
force-pushed
the
task-history-retention-infp-507
branch
from
October 14, 2026 13:27
b9db2c4 to
938f241
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ition and removed flags Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cleanup Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… own session Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dd a state-list unit test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a task_manager.retention configuration section with task_history (30 days), activity_log (7 days) and prefect_own_events (7 days), read from infrahub.toml or INFRAHUB_TASK_MANAGER_RETENTION_* variables. Values take a number of days (30d) or an ISO 8601 duration (P30D) and must be at least 1 day; any other value fails the configuration load with "Invalid task manager retention: <key> <reason>". An own-event retention longer than the activity log is accepted. The section is built when the configuration loads rather than at import, so an invalid value reaches the friendly load error. The configuration reference generator now resolves the variable names of a nested settings model that has its own prefix, and the compose files declare the new variables. INFP-507 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The task manager now loads the Infrahub configuration in every mode and translates task_manager.retention into Prefect's settings before the app is created: - PREFECT_SERVER_SERVICES_DB_VACUUM_ENABLED=events,flow_runs - PREFECT_SERVER_SERVICES_DB_VACUUM_RETENTION_PERIOD from task_history - PREFECT_SERVER_EVENTS_RETENTION_PERIOD from activity_log - PREFECT_SERVER_SERVICES_DB_VACUUM_EVENT_RETENTION_OVERRIDES, one entry per listed Prefect event type, capped to activity_log Durations are written as ISO 8601 days (P30D, P1DT43200S), the only form both of Prefect's timedelta parsers read: the events retention rejects a number of seconds. A Prefect variable already set in the environment wins, with one warning per variable, and a capped own-event retention logs a warning. An invalid retention stops the process with the configuration error. Prefect builds its global settings from the environment once, when prefect.context is imported, so the values only apply after refresh_global_settings_context(); without it the vacuum keeps Prefect's defaults. The stock Prefect image used by the integration test container has no infrahub package, so it now starts create_prefect_app_with_infrahub_routes, which builds the app without loading the configuration. INFP-507 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mark T003-T007 and T047 done and record what landed: the settings test path under tests/unit/config/, the event types passed to build_prefect_retention_env, the capped own-event warning returned by apply_prefect_retention_env, and the Prefect settings refresh. Correct research R2: Prefect's get_current_settings() returns the global settings context built when prefect.context is imported, so environment changes apply only after refresh_global_settings_context(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A well-formed duration too large to read was reported as malformed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…retention changelog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntion `P` and `PT` matched the duration pattern and were refused as longer than 36500 days. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
saltas888
force-pushed
the
task-history-retention-infp-507
branch
from
October 27, 2026 22:07
938f241 to
a8c7fc9
Compare
… in the task manager The standalone task manager loaded and validated the whole Infrahub configuration, so a setting it does not use, such as an invalid storage driver, stopped it. It now reads only the task_manager section of the configuration file and its INFRAHUB_TASK_MANAGER_* variables. An invalid retention or an unreadable configuration file still stops it, and distributed mode loads the whole configuration as before. The retention settings move to a module that imports nothing else from Infrahub, and infrahub.config imports them from there. The task manager still sets up Infrahub's logging before Prefect builds its app, so the retention lines stay visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…round services With PREFECT__SERVER_WEBSERVER_ONLY set, the task manager runs only the web server, and the process that runs Prefect's background services does not read task_manager.retention, so it deletes no task runs. The task manager now logs a warning at startup that says so, instead of only logging the retention as if it were in effect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…ity_log A prefect_own_events longer than activity_log has no effect, because every event older than activity_log is deleted anyway. Infrahub used to accept it, cap it at runtime and log a warning, and the cap used activity_log even when PREFECT_SERVER_EVENTS_RETENTION_PERIOD was set and activity_log was not in effect. The settings now refuse such a value at startup, and the per-type overrides carry prefect_own_events as configured, since Prefect caps each override by the event retention it uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
A compose or Helm file that passes PREFECT_SERVER_SERVICES_DB_VACUUM_ENABLED through from an unset host variable gives the task manager an empty value. Infrahub kept it as an operator setting, and Prefect read it as false, so finished task runs were never deleted. An empty or blank retention variable is now removed and replaced with the value derived from the task manager retention, with no warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
… turns off A preset PREFECT_SERVER_SERVICES_DB_VACUUM_ENABLED was reported as overriding task_history, although task_history still set the run retention, and a value without events silently stopped all event deletion. The switch is now kept without that warning. After Prefect's settings are refreshed, the task manager warns when the switch turns off the deletion of task runs or events, and names the retention that is not applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…g the values Prefect applies Prefect also reads its settings from .env, prefect.toml, pyproject.toml and the active profile, and an environment variable wins over all of them. A retention set in one of those files was silently replaced by the value Infrahub wrote. The task manager now writes a variable only when no Prefect source sets that setting, warns about the ones it leaves, and logs the values from Prefect's parsed settings after the refresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…retention With the default 7-day activity log and own-event retention, the task manager wrote a per-type override for every listed Prefect event type, so Prefect ran over 200 delete statements every hour that removed nothing its general event cleanup does not remove. The overrides are now written as an empty mapping when the own-event retention equals the activity log retention and Infrahub sets the event retention. The rows deleted do not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…list against Prefect's source Prefect's per-type event retention matches exact names, and the list of Prefect event types missed the two events that prefect transfer sends. They are now listed, with the run events of the Rerunning state name that Prefect's server sets on task runs. New unit tests read the installed Prefect package and fail when it sends an event name, builds a name pattern or defines a status, lifecycle record kind or state name that the list does not cover, so a Prefect upgrade that adds one fails CI instead of keeping those events for the activity log retention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
The test ran Prefect's event cleanup on the database that every component test in the same worker shares, so it deleted any old event another test had seeded there. It now runs on an empty, migrated SQLite database in its own temporary directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…ests The state constructors were typed as returning object, so ty, which CI runs, reported an invalid argument where their states are added to the list of Prefect states. They are now typed as returning Prefect's client or server state. The test docstring and research.md also said the tests read every name built from the known patterns. They do not read the lifecycle actions, the concurrency limit phases, the asset materialization statuses or a name passed through a helper with another parameter name, so the text now says so. plan.md now names the unit tests that check the event type list against Prefect's source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
… an empty retention variable does An empty PREFECT_SERVER_SERVICES_DB_VACUUM_RETENTION_PERIOD or PREFECT_SERVER_EVENTS_RETENTION_PERIOD makes the import of prefect.context fail with a time_delta_parsing error, and an empty PREFECT_SERVER_SERVICES_DB_VACUUM_EVENT_RETENTION_OVERRIDES with a SettingsError. Both happen when the task manager app module or the infrahub command line is imported, before any Infrahub code can remove the variable, so removing them never took effect. Only the vacuum switch is now removed when it is empty. The changelog and the specs now say that an empty vacuum switch gives way to a value from Prefect's settings files or profile, with the usual warning for each cleanup it turns off, and otherwise to events,flow_runs, and that an empty retention variable still stops the task manager at startup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…an activity_log The refusal said the task manager deletes every event older than activity_log, which is false when a Prefect setting already sets the event retention or the vacuum switch turns off the event cleanup. The message now asks the operator to set prefect_own_events to at most the activity_log value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
The loader also raises tomllib.TOMLDecodeError, UnicodeDecodeError and OSError when the configuration file cannot be parsed, decoded or read, so its Raises section now names them. The configuration contract now says that an invalid INFRAHUB_LOG_LEVEL or INFRAHUB_PRODUCTION still stops the standalone task manager, because it reads both variables to set up its logging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…eady asserts The equal_retentions case wrote the 30d, 7d and 7d retentions with nothing preset and checked that the per-type overrides are empty. The test that applies every missing variable uses the same settings and checks the whole environment, overrides included, so it already pinned this. Both failed together when the skip of equal overrides was reverted. The case is removed and the apply test now says why its overrides are empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
Say that refusing a prefect_own_events longer than activity_log is about the case where the task manager sets the event retention, that only an empty vacuum switch in the environment is removed, that an empty retention variable stops the task manager only under the name Prefect reads, and that the standalone task manager also reads the two logging variables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
3 of 5 tasks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
T021 checks that the retention reaches Prefect's background services when they run in their own container, which only the command of T048 does, and plan.md already puts that command in part 1. The implementation strategy, the US1 checkpoint and the delivery order in spec.md now include US4 in part 1, and step 3 covers only US3. T048 now lists the steps of apply_infrahub_settings_to_prefect() from T007, including the refresh of Prefect's global settings, because Prefect reads its settings from the environment once at import and the vacuum services would otherwise keep the values from before the command applied the retention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
… never started No command named stuck-runs exists; the command that marks long RUNNING or PENDING runs as CRASHED is `infrahub tasks flush stale-runs`. data-model.md and T053 now use that name. The spec files said that the command does not catch PENDING runs that never started. In Prefect 3.8.6 the start time filter compares the expected start time when a run has no start time, and Prefect sets an expected start time on the first state of every run. A run against a temporary Prefect server showed that the command's filter matches such a run once that time is older than the cutoff, and that forcing it to CRASHED gives it an end time. The edge case, FR-014 and the CLI contract now say this, and the out-of-scope follow-up is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
- plan.md: the Evidence table said that PR #33's test_activity_log.py had landed with its retention override switched, but T037 and T038 are still open. The row now says that T037 lands and switches it and T038 extends it, before the evidence run. - contracts/configuration.md: name PREFECT_EVENTS_RETENTION_PERIOD, the only legacy name among the four Prefect settings in Prefect 3.8.6, in the precedence list and the warning line. - quickstart.md: step 4 seeds old finished runs that are more than half of the runs on the Compose stack (Postgres), because the rewrite runs only then, and T013 states the same condition. Part 1 now names the unit test that checks Prefect's settings turn on the task run cleanup, and the private test from T021 that checks the hourly cleanup. - research.md R1: Prefect's vacuum_old_flow_runs deletes each batch of runs first and their logs and artifacts after, the order R5 follows. - critique-20261004.md: the correction note names every place that still counts E2 and says that E1 and E4 remain, leaving the dated review as written. - spec.md FR-005 says "the task manager's cutoff date", and the checklist notes list each technical detail the spec keeps on purpose, and why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…etention tests The fixture put the earlier values back before pytest undid the tests' setenv calls, and that undo then deleted them, so a retention variable set before the tests, such as the vacuum switch that the component, functional and integration conftests set, was gone after this file ran. The fixture now clears the variables with monkeypatch.delenv, so pytest restores them, and at teardown only removes the values the code under test writes directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
…sts once test_retention.py pinned the names as literals while test_app_retention.py imported them from the module under test, and both files declared the legacy event retention name and the Prefect test mode variables. The names now live once, as literals, in tests/helpers/task_manager_retention.py, so a misspelled name in the code under test still fails the tests. The fixture also uses the constant for PREFECT__SERVER_WEBSERVER_ONLY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UwJNFw8rccyxc4Wp9eZFbH
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The task manager never deletes finished task runs, so task history grows until the disk fills, and the activity log is fixed at Prefect's 7 days with no Infrahub setting. Design doc: Task history and Activity log retention; spec set in
dev/specs/infp-507-task-history-retention/.Goal: one Infrahub setting per kind of record, translated into the Prefect settings that enforce it, so Prefect's built-in hourly cleanup bounds task history from this PR on.
Non-goals here: deleting the existing backlog and giving disk space back (PR 2), the Activities query changes (PR 3).
Closes part of INFP-507.
How retention works
Infrahub defines the retention policy; Prefect's background services enforce it. Task history and activity are kept independently, while Infrahub and Prefect events share the same storage.
flowchart TB CONFIG["Infrahub retention settings<br/>Task history · Activity log · Prefect events"] TYPES["Infrahub-maintained list<br/>of Prefect event types"] subgraph TM["Task manager"] APPLY["At startup<br/>Translate Infrahub settings into Prefect settings"] subgraph SERVICES["Prefect background services — every hour"] TASKS["Task-history cleanup<br/>Remove finished runs older than task_history<br/>together with their logs and artifacts"] EVENTS["Event cleanup<br/>Keep Infrahub events for activity_log<br/>Keep listed Prefect events for prefect_own_events"] end APPLY --> TASKS APPLY --> EVENTS end subgraph DB["Prefect database"] RUNS[("Task history<br/>Runs, states, logs and artifacts")] LOG[("Shared event storage<br/>Infrahub activity + Prefect internal events")] end CONFIG --> APPLY TYPES --> APPLY TASKS -->|"Delete expired task history"| RUNS EVENTS -->|"Delete expired events and related resources"| LOGIncreasing activity-log retention can preserve a year of Infrahub activity without also keeping a year of Prefect's internal events. When background services run in their own deployment, #10878 applies the same settings before starting them.
The operator cleanup in #10869 is a separate recovery step for existing instances: clear the old task-history backlog and recover disk space after an upgrade or retention reduction. The upgrade prints guidance; ongoing retention is handled automatically by the flow above.
What changed
Behavioural changes:
task_manager.retention.task_history(30 days),activity_log(7 days, unchanged from today) andprefect_own_events(7 days); env varsINFRAHUB_TASK_MANAGER_RETENTION_*;30dor ISO 8601; 1 to 36,500 days, or the task manager refuses to start.prefect_own_events; Infrahub events followactivity_log. With the defaults both are 7 days, as today.PREFECT_*variable already set (under any name Prefect accepts, including legacy aliases) takes precedence, with one warning per variable. The effective values are logged at startup.Implementation notes:
backend/infrahub/prefect_server/retention.pybuilds the four Prefect variables;create_infrahub_prefectnow loads the Infrahub configuration in every mode (it only did in distributed mode), applies them, then refreshes Prefect's global settings context (Prefect reads its settings once at import).infrahubpackage, so it now usescreate_prefect_app_with_infrahub_routes, which skips the configuration step.task-managerservice does not merge the shared Infrahub anchor, so the three variables are declared on it explicitly (rootdocker-compose.ymland the dev deps files).tasks/docs.py: the configuration reference showed bare field names for a nested settings model with its own env prefix; it now shows theINFRAHUB_TASK_MANAGER_RETENTION_*names.What stayed the same: no change to Prefect's database schema; Infrahub events are untouched; the component-test harness still turns the vacuum off (its preset variable wins by design).
How to review
backend/infrahub/config.py(settings and validation), thenprefect_server/retention.pyandprefect_server/app.py.tests/unit/config/test_task_manager_retention_settings.py,tests/unit/prefect_server/(one test feeds the derived env into Prefect's own settings model, so a format mistake fails CI),tests/component/task_manager/test_event_retention.py(runs Prefect's event vacuum with a 365-day activity log and 7-day own events).dev/specs/infp-507-task-history-retention/is the spec set for the whole stack.Worth extra scrutiny: the event-type list. Overrides match event names exactly, and an unlisted Prefect type is kept for the activity log retention. A unit test covers every built-in state name; PR 4 adds a full-stack guard on a real workload.
How to test
Manual: start the task manager with
INFRAHUB_TASK_MANAGER_RETENTION_TASK_HISTORY=1dand look for theTask manager retention:lines; with0dit refuses to start.Impact & rollout
task_historyfirst (changelog says so).PREFECT_*precedence documented.Validated locally: unit and component tests,
ruff,mypy,docs.validate,validate-dockercomposeenv; reviewed with cubic (two passes, findings addressed). Release evidence on restored backups comes from opsmill/infrahub-private-tests, run separately.Checklist
🤖 Generated with Claude Code