Repository navigation
feat(continuous-ops): fail loudly on what's required: one preflight, a lean config (XWING-2440) - #133
Conversation
…nfig (XWING-2440)
…2440) Pin core to e8ab152 (the preflight door, core PR 312; contracts PR 102 transitively at 55b69a9), then make the engine refuse before any work: - run, shadow and claim call Context.preflight on the enabled assets' requirements (claim: the assets the runner can claim) and exit 2 naming every gap with its fix - an enabled asset without run or model.role is a pack error at Pack.load; enabled: false is the only exemption (not planned, not claimed, not preflighted) - refuse to start when the pack sets route_on and no --router is given - a route to a lane the pack runs no asset for fails the pass instead of warning and carrying on
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: smith-xyz The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
PR Summary by QodoGate continuous-ops starts on preflight and required pack configuration
AI Description
Diagram
High-Level Assessment
Files changed (12)
|
Code Review by Qodo
🔴 High 1. A misspelled routing asset crashes pack loading
|
…st-engine into feat/fail-loudly-on-what-s-required-one-preflight-a-l
…, #318) — e8ab152 was orphaned by a rebase
chubirka
left a comment
There was a problem hiding this comment.
Code review: changes required
Reviewed commit 84299d5cedad9830c653e83fa30fe05017a7bcfc. Found 1 actionable issue(s); details are attached as inline review comments.
Merge hold: keep this PR in draft with auto-merge disabled until the findings have been addressed and the affected behavior has been re-reviewed. Do not mark ready solely because unrelated CI passes.
|
|
||
| [tool.uv.sources] | ||
| traust-core = { git = "https://github.com/traust-security/traust-core.git", rev = "acf67b8a4409ed7afa1b79d7954636b5b169fa64" } | ||
| traust-core = { git = "https://github.com/traust-security/traust-core.git", rev = "09ad226d4887712ccd1096eb98169729f61a9980" } |
There was a problem hiding this comment.
[P1] Pin the preflight implementation that audits the supplied core home
The newly added run/shadow/claim gate is shipped against core 09ad226, whose preflight calls config_completeness_audit() without the Context's loaded home. As a result --core does not determine what the gate audits: a fully configured explicit --core is refused if TRAUST_CONFIG_HOME points at an incomplete unrelated home (or no default home exists). Conversely the audit can inspect a different complete deployment. This breaks the documented argument-driven engine startup contract and is masked by tests/conftest.py populating a separate ambient home. Re-pin core and the lockfile to a commit containing the loaded-home preflight fix, or bind the audit explicitly to the supplied configuration.
Evidence: ops/cli.py lines 217 and 232/235 load Context.from_file(args.core) then call its new preflight. The actual manifest and lockfile both pin 09ad226d4887712ccd1096eb98169729f61a9980; at that exact commit context/preflight.py line 45 calls config_completeness_audit() without config_home. Core PR312's reviewed be35227 head already supplies ctx.config.config_home, but that implementation is not what this PR installs. README lines 66-68 state everything the service needs comes from its arguments.
Verification: Executed RequiredPreflight-engine-repro.py using engine head 84299d5 and its actual core pin 09ad226, with real complete traust.yaml, model registry, minimal enabled pack, and a separate incomplete TRAUST_CONFIG_HOME. shadow --core /traust.yaml --pack --once returned 2 naming all three locations and ledger.public_key_path as unset despite their presence in the explicitly loaded file. The gate refused before creating the configured store.
There was a problem hiding this comment.
Agreed. The existing #133 head still pins 09ad226 and the review hold stands. In its plan worktree I have changed the pin to core#327 (282ebbee) and regenerated the lockfile, but have NOT pushed: the new core readiness audit exposes test-fixture/config-home gaps and a disabled-route-on behavior expectation that require explicit test/behavior review. I will not claim this resolved or bypass the tests to get green.
|
Addressed the three pending Qodo findings in ce9f2f5: pack validation now rejects an enabled asset depending on a disabled asset (and disabled |
Code Review by Qodo
1. Disabled assets leave orders stranded
|
Plan:
plans/traust-standardization/0-continuous-ops/in-progress/22-shaun-fail-loud-preflight.mdStarted by shaun. Draft until ready for review. This PR is the plan's step 4, the traust-engine part only: the start gate on core's preflight. The traust step (doctor on the same door) is a later PR pinned to this one.
Core pin: 09ad226 — core#312's head (contains #311, #318; the
Context.preflightdoor; contracts at 726d273). The previous pin e8ab152 was orphaned by a rebase, so the branch is re-pinned. Engine #132 and this PR now carry the identical pin 09ad226; merge after the core PRs.What
run,shadowandclaimcallContext.preflighton the pack's enabled assets' requirements and exit non-zero with the full gap list before planning anything (ops/cli.py);claimchecks the assets the claim runner can claim (runnable), a pass checks every enabled asset. The CLI registers core's shipped toolchain so tool requirements are checked against the shipped pins.enabled: falseis the only exemption, filtered at the pack layer (ops/pack.py::requirements): a disabled asset is not preflighted, not planned, not infreshness.failoverdue, not claimable, and a route to its lane plans nothing.runormodel.roleis a pack error atPack.load(namingenabled: falseas the exemption); the "unrunnable → warning, keep ticking" path is gone (Pack.unrunnabledeleted,TickReport.unrunnablewith it).route_onand no--routeris given (aConfigErrornaming the flag), instead of_routesilently returning with routing off.ConfigErrorthat fails the pass before planning anything (_routeruns ahead of the planning loop), instead of a warning the run carries on past.upstream-staleorders, an explicit degraded outcome.traust.yaml(locations + the ledger key — the audited fields) so core's preflight, which promotes the completeness audit's warnings to findings, passes for the CLI tests.No new or changed repository queries: preflight's facts check reuses core's read-only
artifacts.latest_importeddoor, so notest-repositories/perfrun.make lint-fix && make test: 158 passed; the clean base (old pin, no changes): 146 passed — no merge skew.Deviations to look at:
ConfigErrorabove, raised at the first route (still before any planning in that pass). A true load-time check needs the router's lane set, which the coreRouterprotocol does not expose — that would be a core door (noted as a follow-up), not something this engine PR can invent.enabled: falseis declared-optional in config (the plan's reviewer check allows exactly that), so a route into a disabled asset's lane plans nothing, matching what the old unrunnable skip did.Reviewer checks (from the plan)
except: return defaultorlog.warningfollowed by carrying on, for anything required.Quality checklist
Legacy files
Each file this plan replaces: reuse (core already does it), retire (the model made it obsolete) or port.
src/traust_engine/ops/pack.py::unrunnable+ the controller'slog.warning("not planned, no run or model.role")runormodel.roleis a pack error atPack.load;enabled: falseis the exemption. The warn-and-carry-on path (weeks of partial data that looks complete) is goneops/controller.py::_route's silent early return whenroute_onis set and no router was givenrun/shadowrefuse with aConfigErrornaming--routerops/controller.py'slog.warning("routed lanes no pack asset runs")ConfigErrorraised by the tick before planning anything: router lanes must be a subset of the pack's lanesChanged tests
Every existing test edited or deleted, with why (each is the plan's step-4 decision; new tests are not listed):
tests/test_engine_loop.py::test_a_route_to_an_unrunnable_lane_plans_nothing_for_ittest_a_route_to_a_disabled_assets_lane_plans_nothing_for_it; builtiac_lanewithoutrun(assertedpack.unrunnable()) → now setsenabled: falseon itenabled: falseexemptiontests/test_engine_loop.py::test_a_route_to_a_lane_no_asset_runs_plans_nothing_for_ittest_a_route_to_a_lane_no_asset_runs_fails_the_pass; asserted the "routed lanes no pack asset runs" warning and that the run carried on → now expectsConfigErrortests/test_engine_loop.py::test_an_asset_no_runner_can_make_is_reported_not_plannedtest_a_disabled_asset_is_not_planned_or_reported;repo_statewithoutrunwas reportedunrunnableand skipped every tick → the test now disables it (enabled: false, withrequiresandfreshness.fail) and asserts not planned, not overduerun/model.rolenow failsPack.load(covered intest_pack.py); the kept-in-pack-without-requirements behaviour is the exemptiontests/test_engine_loop.py::test_a_route_to_a_lane_no_runner_can_make_plans_nothingdiff_scanwithoutrun, an enabled asset) is now a pack error atPack.load(covered bytest_pack.py::test_an_enabled_asset_without_run_or_model_role_is_a_pack_error); the lane-not-planned behaviour is covered by the two rewrites abovetests/test_ops_shadow.py::test_shadow_prints_what_a_pass_would_plan_and_diff_compares_itroute_on) with no--router→ runs on the same pack withoutroute_on(new_unrouted_packhelper; the_clihelper gained apackkeyword, default unchanged)route_onnow refuses to start without--routertests/test_ops_shadow.py::test_run_once_plans_and_admitsrun --onceon the unrouted packNew tests:
tests/test_ops_preflight.py(10: run and shadow refuse before planning naming every gap — gitleaks uninstalled plus an unrecorded adopter fact — one case per requirement kind crossing the engine boundary, a met requirement lets the engine start, claim checks only the assets it can claim, a routing pack without a router refuses to start),tests/test_pack.py(2: the pack error and theenabled: falseexemption),tests/test_ops_runner.py(1: a disabled asset is not claimable).Cross-repo coordination
Track:
development. Use these explicit related-repository targets rather than assuming the connected repository'smainis the matching overhaul baseline.Review targets
Integration and rollout
Adopt the Core #312 preflight door before the start gate. The application companion checks the same pack/preflight contract; this is shared behavior, not a new Python import from Engine into the application.
Branch URLs are compatibility baselines; PR URLs are proposed companion code. Neither proves that a manifest/lockfile pin already contains the changes or that the target is deployed. Honor the prerequisites above, reconcile compatible dependency pins before rollout, and do not dismiss a real compatibility gap merely because another PR is planned.