)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"5653400094b93d8dfeb2ddeee752564cb4f5a3ce","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     ef99be64 (Add metric query methods to BaseStrategy)"},{"line_number":2,"context_line":"Author:     Douglas Viroel \u003cviroel@gmail.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-07-03 14:35:01 -0300"},{"line_number":4,"context_line":"Commit:     Douglas Viroel \u003cviroel@gmail.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":15,"id":"96f71ae3_9ad65594","line":1,"updated":"2026-08-27 21:11:30.000000000","message":"The commit message says the audit_pipelines table is \u0027(uuid, state, audit_type, execution_mode, auto_trigger, hostname, status_message; uuid/state/audit_type non-nullable)\u0027 - it omits the name column entirely, and the migration actually also makes name, execution_mode, and auto_trigger NOT NULL. For audit_pipeline_stages it lists \u0027(uuid, position, name, goal, strategy, scope, parameters; uuid non-nullable)\u0027 while the real columns are audit_pipeline_id, goal_id, strategy_id and description (goal/strategy are relationships, not columns), and NOT NULL applies to uuid, position, name, goal_id, and audit_pipeline_id as well.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: The permanent change history understates the schema constraints operators and future reviewers rely on when reasoning about upgrade impact and about which columns may be omitted on insert - particularly misleading given the name nullability question is already inconsistent between the migration and the models.\n\n**Suggestion**:\nAmend the message (or correct it in a follow-up note) to list the actual columns and NOT NULL constraints for both tables, including name, and use the real column names goal_id/strategy_id/audit_pipeline_id/description.","commit_id":"67133898f50c5315e636c3718a3b21eca9e2d76b"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"da21fc98e629b45c71343e6ed19614b91556c991","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     85992612 (Migrate package metadata to pyproject.toml)"},{"line_number":2,"context_line":"Author:     Douglas Viroel \u003cviroel@gmail.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-09-01 18:42:07 -0300"},{"line_number":4,"context_line":"Commit:     Douglas Viroel \u003cviroel@gmail.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":20,"id":"ccc637ad_bed9fd91","line":1,"updated":"2026-09-02 23:39:25.000000000","message":"The commit message\u0027s \u0027DB API\u0027 section states destroy \u0027(cascades to stages)\u0027 and soft-delete \u0027(cascades soft-delete to stages)\u0027. The SQLAlchemy implementations do neither: destroy_audit_pipeline only deletes the pipeline row (see CF-001), and soft_delete_audit_pipeline calls the generic _soft_delete helper that soft-deletes exactly one row. The stage cascade actually lives in the object layer (AuditPipeline.soft_delete()/destroy()), which the new DB test itself acknowledges: \u0027The DB method soft-deletes only the pipeline row; the stage cascade lives in the object layer\u0027. The permanent change history therefore misdescribes which layer owns the cascade behavior.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: Reviewers and future maintainers reading the change history will assume the DB API enforces stage cascade, and may call the DBAPI directly instead of the object layer, hitting the FK failure of CF-001 or leaving orphaned live stage rows after soft-delete.\n\n**Suggestion**:\nReword the commit message to state that the cascade is implemented in the AuditPipeline object layer (soft_delete/destroy destroy stages explicitly), not in the DB API methods - or, better, fix the DB API to match the message as recommended in CF-001.","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"50d2e8219e23418f5371b2ae6e989736fe673750","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"5b070762_385c9a83","updated":"2026-08-05 15:06:32.000000000","message":"Thanks Alfredo, pushing a new PS to add the constraint check","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":14,"id":"40251c06_22a75c8b","updated":"2026-08-27 21:46:28.000000000","message":"Thanks for the reviews","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"316e1e52a4b71f114546b123c701c828b9a18a66","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":17,"id":"128153ef_cb943b79","updated":"2026-09-01 13:16:43.000000000","message":"looks good to me, and all the open comments appear adressed","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"da30c9edb6f8a06d01409de5b05cb089e7a72998","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":18,"id":"bf8874a3_219c5bae","updated":"2026-09-02 11:53:38.000000000","message":"+1 beacuse you have left several comment about change you plan to do which i agree with","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"fee626e5fe703265b4730ec6f77d9358920e8b61","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":18,"id":"a2e75829_b57a57b7","updated":"2026-09-02 11:05:47.000000000","message":"Sean and Joan, in the end I didn\u0027t split this in 2 changes, mainly because the DB change in Action Plan affects unit tests in Action Plan objects, so I would need to drop some dict validation in unit tests to revert later in the next change. So in the end I keep them here in the same change.","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"f86cbf19ddcef2f0111f78a4c01ed0f1bb67aa45","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":19,"id":"aa2e3590_7ce4701c","updated":"2026-09-02 17:39:07.000000000","message":"looks good, the open conversion point to improvements that can be done in a followup or as tech debt","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"225683451a830e23d4b334d1f6b8632de00dd161","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":20,"id":"9eaa0dec_caec692b","updated":"2026-10-20 13:38:08.000000000","message":"Lets hold since I plan to include a \"description\" field in Audit Pipeline.","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1fe788d3b6d87e450b6050f4ca0a6e68a0424606","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":20,"id":"22d418a6_5d8ec055","updated":"2026-09-03 10:50:39.000000000","message":"i dont see anything that cant be adressed in a followup so im ok with proceeding with this version","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"ced52e2072df907496fdb724d4cf1be23fec5b41","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":20,"id":"bd9f8e76_ff879dad","updated":"2026-10-07 12:47:34.000000000","message":"recheck","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"feeeafce1d34a0558acede376d9946adc5bdab3b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":20,"id":"a6880c28_5918dc5c","updated":"2026-10-07 12:48:08.000000000","message":"specc still needs to be merged for 2027.1","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"ad03466c2f13abb260729968e5ea53219954e131","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":20,"id":"f4ac90c0_6240fbb7","updated":"2026-09-03 10:54:54.000000000","message":"thanks Doug, I think we can merge and refine the open thread on a follow-up patch","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"84127cfc4f54a5fa0d0a14b6b5a56d51322b3c46","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":21,"id":"b1720fdc_08cc6f51","updated":"2026-10-27 08:16:01.000000000","message":"lgtm","commit_id":"f4ce2284b27926a90bf9f96d59584fb47ded60b8"}],"watcher/db/api.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"46bc16e50d9b02e033c33eb0cef2a9e0ee579b07","unresolved":false,"context_lines":[{"line_number":418,"context_line":"        \"\"\""},{"line_number":419,"context_line":""},{"line_number":420,"context_line":"    @abc.abstractmethod"},{"line_number":421,"context_line":"    def get_audit_pipeline_by_uuid("},{"line_number":422,"context_line":"        self, context, audit_pipeline_uuid, eager\u003dFalse"},{"line_number":423,"context_line":"    ):"},{"line_number":424,"context_line":"        \"\"\"Return an audit pipeline."}],"source_content_type":"text/x-python","patch_set":12,"id":"a0191ec0_f6357d2a","line":421,"updated":"2026-08-19 20:36:29.000000000","message":"The abstract BaseConnection declares get_audit_pipeline_by_id and get_audit_pipeline_by_uuid but not get_audit_pipeline_by_name, while the SQLAlchemy implementation defines it and objects.AuditPipeline.get_by_name calls it. The backend contract no longer covers a method the object layer depends on.\n\n**Severity**: SUGGESTION | **Confidence**: 0.85\n\n**Impact**: An alternative backend implementing only the abstract contract would lack get_audit_pipeline_by_name and break AuditPipeline.get_by_name; the invariant that BaseConnection mirrors the consumed surface has multiple owners that can drift.\n\n**Recommendation**:\nAdd an @abc.abstractmethod get_audit_pipeline_by_name(self, context, audit_pipeline_name, eager\u003dFalse) with a docstring mirroring the implementation, placed between get_audit_pipeline_by_uuid and destroy_audit_pipeline.","commit_id":"bbcfe5be54863e5aa876ecbb5a9b8eb8db2e2a4f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":367,"context_line":"        :raises: :py:class:`~.AuditTemplateNotFound`"},{"line_number":368,"context_line":"        \"\"\""},{"line_number":369,"context_line":""},{"line_number":370,"context_line":"    # \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"},{"line_number":371,"context_line":"    # AuditPipeline"},{"line_number":372,"context_line":"    # \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":"    @abc.abstractmethod"},{"line_number":375,"context_line":"    def get_audit_pipeline_list("}],"source_content_type":"text/x-python","patch_set":14,"id":"cdf2c9fa_2b643e85","line":372,"range":{"start_line":370,"start_character":2,"end_line":372,"end_character":79},"updated":"2026-08-26 20:43:47.000000000","message":"ai likes to do this but we shoudl not add block comments like this","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":367,"context_line":"        :raises: :py:class:`~.AuditTemplateNotFound`"},{"line_number":368,"context_line":"        \"\"\""},{"line_number":369,"context_line":""},{"line_number":370,"context_line":"    # \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"},{"line_number":371,"context_line":"    # AuditPipeline"},{"line_number":372,"context_line":"    # \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":"    @abc.abstractmethod"},{"line_number":375,"context_line":"    def get_audit_pipeline_list("}],"source_content_type":"text/x-python","patch_set":14,"id":"f1145d23_6cdf5b10","line":372,"range":{"start_line":370,"start_character":2,"end_line":372,"end_character":79},"in_reply_to":"cdf2c9fa_2b643e85","updated":"2026-08-27 12:25:09.000000000","message":"I found it useful in long files, but it could also be only:\n\u0027# AuditPipeline\u0027","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":false,"context_lines":[{"line_number":367,"context_line":"        :raises: :py:class:`~.AuditTemplateNotFound`"},{"line_number":368,"context_line":"        \"\"\""},{"line_number":369,"context_line":""},{"line_number":370,"context_line":"    # \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"},{"line_number":371,"context_line":"    # AuditPipeline"},{"line_number":372,"context_line":"    # \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":"    @abc.abstractmethod"},{"line_number":375,"context_line":"    def get_audit_pipeline_list("}],"source_content_type":"text/x-python","patch_set":14,"id":"bea65316_48d3e117","line":372,"range":{"start_line":370,"start_character":2,"end_line":372,"end_character":79},"in_reply_to":"f1145d23_6cdf5b10","updated":"2026-08-27 21:46:28.000000000","message":"Done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":false,"context_lines":[{"line_number":371,"context_line":"    # AuditPipeline"},{"line_number":372,"context_line":"    # \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d"},{"line_number":373,"context_line":""},{"line_number":374,"context_line":"    @abc.abstractmethod"},{"line_number":375,"context_line":"    def get_audit_pipeline_list("},{"line_number":376,"context_line":"        self,"},{"line_number":377,"context_line":"        context,"},{"line_number":378,"context_line":"        filters\u003dNone,"}],"source_content_type":"text/x-python","patch_set":14,"id":"a76789d2_2ec27176","line":375,"range":{"start_line":374,"start_character":2,"end_line":375,"end_character":32},"updated":"2026-08-26 20:43:47.000000000","message":"i noted this on irc, for now this is requried but we really shoudl remove the api since we are not going to implment multie db backend drivers","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":471,"context_line":"        :param marker: The last item of the previous page"},{"line_number":472,"context_line":"        :param sort_key: Attribute by which results should be sorted"},{"line_number":473,"context_line":"        :param sort_dir: Direction in which results should be sorted"},{"line_number":474,"context_line":"        :param eager: Load object fields if True (Default: False)"},{"line_number":475,"context_line":"        :returns: A list of :py:class:`~.AuditPipelineStage` objects"},{"line_number":476,"context_line":"        \"\"\""},{"line_number":477,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"7d58eeb1_cdf034be","line":474,"updated":"2026-09-01 16:27:25.000000000","message":"this does not feel like the right interface to me but we can adress that later\n\nif we want to supprot laze loadign fo filed they 1 need to be nullabel in teh obejct defaiont and 2 instead of being a bool we shoudl pass a list of the optional/extra field to load rather then a course grained boolean\n\nfor a first pass i woudl not have  implemnted eager and i woudl have defaulted to True if i did include it.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":471,"context_line":"        :param marker: The last item of the previous page"},{"line_number":472,"context_line":"        :param sort_key: Attribute by which results should be sorted"},{"line_number":473,"context_line":"        :param sort_dir: Direction in which results should be sorted"},{"line_number":474,"context_line":"        :param eager: Load object fields if True (Default: False)"},{"line_number":475,"context_line":"        :returns: A list of :py:class:`~.AuditPipelineStage` objects"},{"line_number":476,"context_line":"        \"\"\""},{"line_number":477,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"7f30808b_789a7a43","line":474,"in_reply_to":"7d58eeb1_cdf034be","updated":"2026-09-01 20:00:06.000000000","message":"ack, makes sense, we may need to follow up as tech debt in all","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":471,"context_line":"        :param marker: The last item of the previous page"},{"line_number":472,"context_line":"        :param sort_key: Attribute by which results should be sorted"},{"line_number":473,"context_line":"        :param sort_dir: Direction in which results should be sorted"},{"line_number":474,"context_line":"        :param eager: Load object fields if True (Default: False)"},{"line_number":475,"context_line":"        :returns: A list of :py:class:`~.AuditPipelineStage` objects"},{"line_number":476,"context_line":"        \"\"\""},{"line_number":477,"context_line":""}],"source_content_type":"text/x-python","patch_set":17,"id":"9c4fedb7_c71a5af9","line":474,"in_reply_to":"7f30808b_789a7a43","updated":"2026-09-02 19:41:05.000000000","message":"Acknowledged lets revisit this then and keep the current scope small","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"}],"watcher/db/sqlalchemy/alembic/versions/4194bfa10c1a_add_audit_pipeline_tables.py":[{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"ff00184210cdad5a0d508202025663d52b9cbc1e","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"5e7ba2d5_37ee23ba","line":100,"updated":"2026-08-04 14:32:44.000000000","message":"Same comment about adding one new constraint.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"06813565f69cd6c3fe654cc2b90c1510c3bd21dd","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"3bde4da2_d8b3073b","line":100,"in_reply_to":"2072e290_1ea687d0","updated":"2026-08-28 15:07:39.000000000","message":"The validation is implemented here:\nhttps://review.opendev.org/c/openstack/watcher/+/996849/14/watcher/objects/action_plan.py#182\nCan we resolve this thread?","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"f3aaebd3_de9498e6","line":100,"in_reply_to":"3bde4da2_d8b3073b","updated":"2026-09-01 14:05:52.000000000","message":"Acknowledged","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"50d2e8219e23418f5371b2ae6e989736fe673750","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"f03f2b9a_6a62cf0b","line":100,"in_reply_to":"5e7ba2d5_37ee23ba","updated":"2026-08-05 15:06:32.000000000","message":"You mean about audit_id and audit_pipelie_id not being both null or both set right?\nI think that at DB table level is hard to maintain, if we think that these tables can change in the future and the constraint also change, it would be harder to work with migrations. So I would prefer to guarantee that constraint at Object level. Let me know if that makes sense.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"2072e290_1ea687d0","line":100,"in_reply_to":"829ed7d0_1042bcf0","updated":"2026-08-26 20:43:47.000000000","message":"we shoudl check in the object before save that only a pipeline or audit is checked\nrather then validting that in a the db with an insert or update trigger.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"c06de61bbd660a009b2bd836baff47bc37600cf8","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":3,"id":"829ed7d0_1042bcf0","line":100,"in_reply_to":"f03f2b9a_6a62cf0b","updated":"2026-08-06 11:14:45.000000000","message":"I\u0027m not sure what\u0027s the best level to implement the constraint, i tend to think that the lower the better, but you are right that it make harder to change in future, although I\u0027d say once a constraint is added, future ones should not be contraditories with previous. WRT object level or dbapi, i\u0027m also not sure, will every call to dbapi  will use the object?\n\nI\u0027d like to get opinion from @smooney@redhat.com about this.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"3231bcd0351d5dd9b559ce54be11c021f4495731","unresolved":false,"context_lines":[{"line_number":30,"context_line":"down_revision \u003d \u0027f56ba02662b4\u0027"},{"line_number":31,"context_line":""},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"def upgrade():"},{"line_number":34,"context_line":"    op.create_table("},{"line_number":35,"context_line":"        \u0027audit_pipelines\u0027,"},{"line_number":36,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":7,"id":"6dceda7f_cbb0e5e7","line":33,"updated":"2026-08-12 12:42:54.000000000","message":"The new Alembic migration creates two tables and modifies action_plans (adds column, FK, and makes two columns nullable) but provides no downgrade() function to reverse these changes.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: If the migration needs to be rolled back (e.g., during a failed deployment or upgrade), there is no automated way to revert the schema changes. The action_plans column nullability changes (audit_id, strategy_id made nullable) are particularly important to be able to reverse.\n\n**Suggestion**:\nAdd a downgrade() function that reverses all operations: drop the FK and audit_pipeline_id column from action_plans, restore audit_id and strategy_id to nullable\u003dFalse, and drop both audit_pipeline_stages and audit_pipelines tables.","commit_id":"789c2a9522ce4a9cf453ec24251c64a9a7b7736c"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"ef239701054fbd6315679a4b338b4d290ed8a214","unresolved":false,"context_lines":[{"line_number":88,"context_line":"        [\u0027id\u0027],"},{"line_number":89,"context_line":"    )"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"    op.alter_column("},{"line_number":92,"context_line":"        \u0027action_plans\u0027, \u0027audit_id\u0027, nullable\u003dTrue, existing_type\u003dsa.Integer()"},{"line_number":93,"context_line":"    )"},{"line_number":94,"context_line":"    op.alter_column("}],"source_content_type":"text/x-python","patch_set":8,"id":"ac5db068_0f53b3e0","line":91,"updated":"2026-08-12 22:50:00.000000000","message":"The migration alters the action_plans table to make audit_id and strategy_id nullable, which is an upgrade-impacting schema change. The commit message lacks an UpgradeImpact footer and no release note was added to document the schema relaxation and new tables.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Operators upgrading may not be aware of the schema change. The relaxation of audit_id/strategy_id to nullable changes data-model semantics that downstream consumers or tooling may depend on.\n\n**Suggestion**:\nAdd an UpgradeImpact footer to the commit message and create a release note (releasenotes/notes/audit-pipeline-tables-\u003chash\u003e.yaml) documenting the new audit_pipelines and audit_pipeline_stages tables and the nullable change on action_plans.audit_id and action_plans.strategy_id.","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"d5434540657b9eab1fb2e573c09aa8a62f89b12d","unresolved":false,"context_lines":[{"line_number":88,"context_line":"        [\u0027id\u0027],"},{"line_number":89,"context_line":"    )"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"    op.alter_column("},{"line_number":92,"context_line":"        \u0027action_plans\u0027, \u0027audit_id\u0027, nullable\u003dTrue, existing_type\u003dsa.Integer()"},{"line_number":93,"context_line":"    )"},{"line_number":94,"context_line":"    op.alter_column("}],"source_content_type":"text/x-python","patch_set":8,"id":"89507644_c1718f0c","line":91,"in_reply_to":"ac5db068_0f53b3e0","updated":"2026-08-13 14:42:46.000000000","message":"ack, can be added to the release notes of the last patch in the series","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":33,"context_line":"def upgrade():"},{"line_number":34,"context_line":"    op.create_table("},{"line_number":35,"context_line":"        \u0027audit_pipelines\u0027,"},{"line_number":36,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":37,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"f9eb7a96_fc5c2390","line":36,"range":{"start_line":36,"start_character":0,"end_line":36,"end_character":62},"updated":"2026-08-26 20:43:47.000000000","message":"created_at does not need ot be nullable althguh updated at and delete shouldbe\nare you using nullable\u003dtrue becasue fo the softdelete mixin/base class?","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":false,"context_lines":[{"line_number":33,"context_line":"def upgrade():"},{"line_number":34,"context_line":"    op.create_table("},{"line_number":35,"context_line":"        \u0027audit_pipelines\u0027,"},{"line_number":36,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":37,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"40474d62_2f4adc85","line":36,"range":{"start_line":36,"start_character":0,"end_line":36,"end_character":62},"in_reply_to":"b5aeac49_304451f6","updated":"2026-08-27 21:46:28.000000000","message":"Done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":33,"context_line":"def upgrade():"},{"line_number":34,"context_line":"    op.create_table("},{"line_number":35,"context_line":"        \u0027audit_pipelines\u0027,"},{"line_number":36,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":37,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"b5aeac49_304451f6","line":36,"range":{"start_line":36,"start_character":0,"end_line":36,"end_character":62},"in_reply_to":"f9eb7a96_fc5c2390","updated":"2026-08-27 12:25:09.000000000","message":"For sure it will always be populated, it is just following the rest of the tables, but I can update it yeah.","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":37,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"7b00fb34_a7df924b","line":40,"range":{"start_line":40,"start_character":22,"end_line":40,"end_character":37},"updated":"2026-08-26 20:43:47.000000000","message":"so in nova we have some tabel that hit max int\n\ni think we shoudl use bigint for all primary key filed going forward in watcher as well.","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":37,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"6c64278a_21182a7d","line":40,"range":{"start_line":40,"start_character":22,"end_line":40,"end_character":37},"in_reply_to":"611c2117_189a6840","updated":"2026-09-01 14:05:52.000000000","message":"Acknowledged","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":37,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"c1987e43_08f41983","line":40,"range":{"start_line":40,"start_character":22,"end_line":40,"end_character":37},"in_reply_to":"7b00fb34_a7df924b","updated":"2026-08-27 12:25:09.000000000","message":"ack, we can, as also we can update the other ones in a new patch.","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":true,"context_lines":[{"line_number":37,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"611c2117_189a6840","line":40,"range":{"start_line":40,"start_character":22,"end_line":40,"end_character":37},"in_reply_to":"c1987e43_08f41983","updated":"2026-08-27 21:46:28.000000000","message":"Updated, also updated deleted since it would copy the id when soft deleting.","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":44,"context_line":"        sa.Column(\u0027state\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":45,"context_line":"        sa.Column(\u0027audit_type\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"fd116d25_9a6d7308","line":42,"range":{"start_line":42,"start_character":5,"end_line":42,"end_character":63},"updated":"2026-08-26 20:43:47.000000000","message":"all pipelines shoudl be named so this should not be nullable right?","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":44,"context_line":"        sa.Column(\u0027state\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":45,"context_line":"        sa.Column(\u0027audit_type\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"963341ed_cdafcf0d","line":42,"range":{"start_line":42,"start_character":5,"end_line":42,"end_character":63},"in_reply_to":"b0de4b4c_5ffbc9a9","updated":"2026-09-01 14:05:52.000000000","message":"Done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":44,"context_line":"        sa.Column(\u0027state\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":45,"context_line":"        sa.Column(\u0027audit_type\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"b0de4b4c_5ffbc9a9","line":42,"range":{"start_line":42,"start_character":5,"end_line":42,"end_character":63},"in_reply_to":"fd116d25_9a6d7308","updated":"2026-08-27 12:25:09.000000000","message":"In audit it is nullable too, it is not mandatory but it is always generated if the user don\u0027t provide it (generated from goal/strategy name), so it always has a name. In the pipeline implementation I\u0027m not generating a new name, and letting it save as null if not provided. We could set this to nullable\u003dFalse and generate a name as Audit does, but wasn\u0027t implemented like that.","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    op.create_table("},{"line_number":57,"context_line":"        \u0027audit_pipeline_stages\u0027,"},{"line_number":58,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":59,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":60,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"69620b9b_7d359fe2","line":58,"range":{"start_line":58,"start_character":6,"end_line":58,"end_character":62},"updated":"2026-08-26 20:43:47.000000000","message":"same as above","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":true,"context_lines":[{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    op.create_table("},{"line_number":57,"context_line":"        \u0027audit_pipeline_stages\u0027,"},{"line_number":58,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":59,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":60,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"8bf823c1_a9078c49","line":58,"range":{"start_line":58,"start_character":6,"end_line":58,"end_character":62},"in_reply_to":"69620b9b_7d359fe2","updated":"2026-09-01 14:05:52.000000000","message":"this was not done but was changed by audit_pipelines is there a reason?","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1bd74e4ba7dddfda18ee818c997e8ac193da957d","unresolved":true,"context_lines":[{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    op.create_table("},{"line_number":57,"context_line":"        \u0027audit_pipeline_stages\u0027,"},{"line_number":58,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":59,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":60,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"fcad53db_d89cef78","line":58,"range":{"start_line":58,"start_character":6,"end_line":58,"end_character":62},"in_reply_to":"8bf823c1_a9078c49","updated":"2026-09-01 15:31:11.000000000","message":"I forgot this one, I can submit a new PS or fix in a follow up (before cutting the release)","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"2fec6ac3408ef465574db4cd14be6b49c2263288","unresolved":false,"context_lines":[{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    op.create_table("},{"line_number":57,"context_line":"        \u0027audit_pipeline_stages\u0027,"},{"line_number":58,"context_line":"        sa.Column(\u0027created_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":59,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":60,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"b1258b22_520a441b","line":58,"range":{"start_line":58,"start_character":6,"end_line":58,"end_character":62},"in_reply_to":"fcad53db_d89cef78","updated":"2026-09-01 22:04:33.000000000","message":"done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":59,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":60,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":62,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"ca357bad_1898721f","line":62,"range":{"start_line":62,"start_character":7,"end_line":62,"end_character":74},"updated":"2026-08-26 20:43:47.000000000","message":"also bigint","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":59,"context_line":"        sa.Column(\u0027updated_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":60,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":62,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":14,"id":"31b66fbd_a713d8b3","line":62,"range":{"start_line":62,"start_character":7,"end_line":62,"end_character":74},"in_reply_to":"ca357bad_1898721f","updated":"2026-09-01 14:05:52.000000000","message":"Done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":62,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":66,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":67,"context_line":"        sa.Column(\u0027description\u0027, sa.String(length\u003d255), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"2661506d_1e937f3e","line":64,"range":{"start_line":64,"start_character":7,"end_line":64,"end_character":69},"updated":"2026-08-26 20:43:47.000000000","message":"bigint","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":61,"context_line":"        sa.Column(\u0027deleted\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":62,"context_line":"        sa.Column(\u0027id\u0027, sa.Integer(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":66,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":67,"context_line":"        sa.Column(\u0027description\u0027, sa.String(length\u003d255), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"be7a7758_bb776816","line":64,"range":{"start_line":64,"start_character":7,"end_line":64,"end_character":69},"in_reply_to":"2661506d_1e937f3e","updated":"2026-09-01 14:05:52.000000000","message":"Done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":66,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":67,"context_line":"        sa.Column(\u0027description\u0027, sa.String(length\u003d255), nullable\u003dTrue),"},{"line_number":68,"context_line":"        sa.Column(\u0027goal_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":69,"context_line":"        sa.Column(\u0027strategy_id\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"9fc79163_85ae38f5","line":66,"range":{"start_line":66,"start_character":5,"end_line":66,"end_character":63},"updated":"2026-08-26 20:43:47.000000000","message":"stages shoudl likely be named as well but they dont need a decrction alwasy","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":true,"context_lines":[{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":66,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":67,"context_line":"        sa.Column(\u0027description\u0027, sa.String(length\u003d255), nullable\u003dTrue),"},{"line_number":68,"context_line":"        sa.Column(\u0027goal_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":69,"context_line":"        sa.Column(\u0027strategy_id\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"bfb40867_3e315e54","line":66,"range":{"start_line":66,"start_character":5,"end_line":66,"end_character":63},"in_reply_to":"79f3a8cd_6f7c67e2","updated":"2026-08-27 21:46:28.000000000","message":"name generation was added for both names, in API patch. There we can discuss more about the audit_pipeline template name. Thanks","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":66,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":67,"context_line":"        sa.Column(\u0027description\u0027, sa.String(length\u003d255), nullable\u003dTrue),"},{"line_number":68,"context_line":"        sa.Column(\u0027goal_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":69,"context_line":"        sa.Column(\u0027strategy_id\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"79f3a8cd_6f7c67e2","line":66,"range":{"start_line":66,"start_character":5,"end_line":66,"end_character":63},"in_reply_to":"9fc79163_85ae38f5","updated":"2026-08-27 12:25:09.000000000","message":"same as AuditPipeline, we would need to generate a name then, but not make it mandaroty from api","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":64,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":65,"context_line":"        sa.Column(\u0027position\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":66,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dTrue),"},{"line_number":67,"context_line":"        sa.Column(\u0027description\u0027, sa.String(length\u003d255), nullable\u003dTrue),"},{"line_number":68,"context_line":"        sa.Column(\u0027goal_id\u0027, sa.Integer(), nullable\u003dFalse),"},{"line_number":69,"context_line":"        sa.Column(\u0027strategy_id\u0027, sa.Integer(), nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":14,"id":"8470074b_43433b5e","line":66,"range":{"start_line":66,"start_character":5,"end_line":66,"end_character":63},"in_reply_to":"bfb40867_3e315e54","updated":"2026-09-01 14:05:52.000000000","message":"Acknowledged","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":73,"context_line":"        sa.ForeignKeyConstraint([\u0027goal_id\u0027], [\u0027goals.id\u0027]),"},{"line_number":74,"context_line":"        sa.ForeignKeyConstraint([\u0027strategy_id\u0027], [\u0027strategies.id\u0027]),"},{"line_number":75,"context_line":"        sa.PrimaryKeyConstraint(\u0027id\u0027),"},{"line_number":76,"context_line":"        sa.UniqueConstraint(\u0027uuid\u0027, name\u003d\u0027uniq_audit_pipeline_stages0uuid\u0027),"},{"line_number":77,"context_line":"    )"},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"    op.add_column("}],"source_content_type":"text/x-python","patch_set":14,"id":"318c639f_7c3837b2","line":76,"range":{"start_line":76,"start_character":7,"end_line":76,"end_character":76},"updated":"2026-08-26 20:43:47.000000000","message":"we shoudl have a uniq constrait on the \n(audit_pipeline_id and position)\nand anohter on \n\n(audit_pipeline_id and name)","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":73,"context_line":"        sa.ForeignKeyConstraint([\u0027goal_id\u0027], [\u0027goals.id\u0027]),"},{"line_number":74,"context_line":"        sa.ForeignKeyConstraint([\u0027strategy_id\u0027], [\u0027strategies.id\u0027]),"},{"line_number":75,"context_line":"        sa.PrimaryKeyConstraint(\u0027id\u0027),"},{"line_number":76,"context_line":"        sa.UniqueConstraint(\u0027uuid\u0027, name\u003d\u0027uniq_audit_pipeline_stages0uuid\u0027),"},{"line_number":77,"context_line":"    )"},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"    op.add_column("}],"source_content_type":"text/x-python","patch_set":14,"id":"adf5e1fc_633e19cd","line":76,"range":{"start_line":76,"start_character":7,"end_line":76,"end_character":76},"in_reply_to":"318c639f_7c3837b2","updated":"2026-08-27 12:25:09.000000000","message":"Ack about Audit_pipeline_id and position, makes sense.\nNot sure aoute audit_pipeline_id and name.\nWhat I now see that is missing is the \"name\" and \"deleted\" to allow unique names","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":73,"context_line":"        sa.ForeignKeyConstraint([\u0027goal_id\u0027], [\u0027goals.id\u0027]),"},{"line_number":74,"context_line":"        sa.ForeignKeyConstraint([\u0027strategy_id\u0027], [\u0027strategies.id\u0027]),"},{"line_number":75,"context_line":"        sa.PrimaryKeyConstraint(\u0027id\u0027),"},{"line_number":76,"context_line":"        sa.UniqueConstraint(\u0027uuid\u0027, name\u003d\u0027uniq_audit_pipeline_stages0uuid\u0027),"},{"line_number":77,"context_line":"    )"},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"    op.add_column("}],"source_content_type":"text/x-python","patch_set":14,"id":"9037a541_f050d530","line":76,"range":{"start_line":76,"start_character":7,"end_line":76,"end_character":76},"in_reply_to":"adf5e1fc_633e19cd","updated":"2026-09-01 14:05:52.000000000","message":"Done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":76,"context_line":"        sa.UniqueConstraint(\u0027uuid\u0027, name\u003d\u0027uniq_audit_pipeline_stages0uuid\u0027),"},{"line_number":77,"context_line":"    )"},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"    op.add_column("},{"line_number":80,"context_line":"        \u0027action_plans\u0027,"},{"line_number":81,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":82,"context_line":"    )"},{"line_number":83,"context_line":"    op.create_foreign_key("},{"line_number":84,"context_line":"        \u0027fk_action_plans_audit_pipeline_id\u0027,"},{"line_number":85,"context_line":"        \u0027action_plans\u0027,"}],"source_content_type":"text/x-python","patch_set":14,"id":"bf50a517_60de8190","line":82,"range":{"start_line":79,"start_character":4,"end_line":82,"end_character":5},"updated":"2026-08-26 20:43:47.000000000","message":"this would work the althernitiv ewoudl be to have a seperate action_plans_pipline mapping table\n\nbut i think this is ok.","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":76,"context_line":"        sa.UniqueConstraint(\u0027uuid\u0027, name\u003d\u0027uniq_audit_pipeline_stages0uuid\u0027),"},{"line_number":77,"context_line":"    )"},{"line_number":78,"context_line":""},{"line_number":79,"context_line":"    op.add_column("},{"line_number":80,"context_line":"        \u0027action_plans\u0027,"},{"line_number":81,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":82,"context_line":"    )"},{"line_number":83,"context_line":"    op.create_foreign_key("},{"line_number":84,"context_line":"        \u0027fk_action_plans_audit_pipeline_id\u0027,"},{"line_number":85,"context_line":"        \u0027action_plans\u0027,"}],"source_content_type":"text/x-python","patch_set":14,"id":"5531df9b_0bed09a3","line":82,"range":{"start_line":79,"start_character":4,"end_line":82,"end_character":5},"in_reply_to":"bf50a517_60de8190","updated":"2026-09-01 14:05:52.000000000","message":"Acknowledged","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"5653400094b93d8dfeb2ddeee752564cb4f5a3ce","unresolved":false,"context_lines":[{"line_number":39,"context_line":"        sa.Column(\u0027deleted\u0027, sa.BigInteger(), nullable\u003dTrue),"},{"line_number":40,"context_line":"        sa.Column(\u0027id\u0027, sa.BigInteger(), autoincrement\u003dTrue, nullable\u003dFalse),"},{"line_number":41,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":42,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dFalse),"},{"line_number":43,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":44,"context_line":"        sa.Column(\u0027state\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":45,"context_line":"        sa.Column(\u0027audit_type\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":15,"id":"a39e3e3b_1df431d9","line":42,"updated":"2026-08-27 21:11:30.000000000","message":"The new alembic migration does not match the SQLAlchemy models that unit tests actually use (tests build the schema via models.Base.metadata.create_all in watcher/db/sqlalchemy/migration.py create_schema, so the drift is never exercised by CI). Divergences: (1) migration declares audit_pipelines.name NOT NULL (line 42) while models.AuditPipeline.name is nullable\u003dTrue (models.py:258) and create_audit_pipeline explicitly supports unnamed pipelines (\u0027name is nullable on AuditPipeline; skip the pre-check when no name is provided to allow multiple unnamed pipelines\u0027); (2) audit_pipeline_stages.name is NOT NULL in the migration (line 66) but nullable\u003dTrue in the model; (3) the migration adds uniq_audit_pipeline_stages0name (name, deleted) and uniq_audit_pipeline_stages0position constraints that do not exist on models.AuditPipelineStage, making stage names globally unique across all pipelines; (4) id/deleted use sa.BigInteger instead of the sa.Integer/SoftDeleteInteger convention used by every other watcher table.\n\n**Severity**: HIGH | **Confidence**: 0.85\n\n**Impact**: On any deployed database (schema created by this migration, unlike test DBs built from models): creating an AuditPipeline without a name - a flow the DB API and the nullable object field explicitly support - fails with an unhandled IntegrityError; creating two pipelines that each contain a stage with the same name (e.g. two \u0027consolidation\u0027 stages) fails with AuditPipelineStageAlreadyExists even though neither the model nor the tests treat stage names as globally unique.\n\n**Priority**: Before merge\n**Recommendation**:\nReconcile the migration with the models: make name nullable\u003dTrue in both tables (or make it required in the model and DB API if unnamed rows are not supported), and either add the matching UniqueConstraints to models.AuditPipelineStage or drop/re-scope the migration-only constraints (stage name/position uniqueness should at minimum be scoped with audit_pipeline_id, i.e. (\u0027audit_pipeline_id\u0027,\u0027position\u0027,\u0027deleted\u0027)). Consider running db tests against the alembic-managed schema (as MySQLDbTestCase does via migration.create_schema) or adding an alembic-vs-models diff check.","commit_id":"67133898f50c5315e636c3718a3b21eca9e2d76b"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"5653400094b93d8dfeb2ddeee752564cb4f5a3ce","unresolved":false,"context_lines":[{"line_number":85,"context_line":"    )"},{"line_number":86,"context_line":""},{"line_number":87,"context_line":"    op.add_column("},{"line_number":88,"context_line":"        \u0027action_plans\u0027,"},{"line_number":89,"context_line":"        sa.Column(\u0027audit_pipeline_id\u0027, sa.Integer(), nullable\u003dTrue),"},{"line_number":90,"context_line":"    )"},{"line_number":91,"context_line":"    op.create_foreign_key("}],"source_content_type":"text/x-python","patch_set":15,"id":"f7088a1a_01cd7c92","line":88,"updated":"2026-08-27 21:11:30.000000000","message":"The migration creates audit_pipelines.id and audit_pipeline_stages.audit_pipeline_id as sa.BigInteger, but adds action_plans.audit_pipeline_id as sa.Integer and creates a foreign key from it to audit_pipelines.id. MySQL/InnoDB requires the referencing and referenced columns of a foreign key to have identical types; INT -\u003e BIGINT mismatch causes the ALTER to fail (errno 150 / error 3780), aborting the whole upgrade. Unit tests never hit this because create_schema() builds tables from model metadata (Integer everywhere) and SQLite does not enforce or support ALTER ADD CONSTRAINT.\n\n**Severity**: HIGH | **Confidence**: 0.75\n\n**Impact**: watcher-db-manage upgrade fails mid-migration on MySQL deployments (new tables already created, then the FK ALTER errors), leaving the database at a partially migrated state and blocking the upgrade entirely.\n\n**Priority**: Before merge\n**Recommendation**:\nUse a single consistent type: either declare action_plans.audit_pipeline_id as sa.BigInteger() to match audit_pipelines.id, or (matching watcher convention and the models) use sa.Integer for audit_pipelines.id, audit_pipeline_stages.audit_pipeline_id, and the FK column so all match.","commit_id":"67133898f50c5315e636c3718a3b21eca9e2d76b"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":true,"context_lines":[{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column("},{"line_number":40,"context_line":"            \u0027deleted\u0027,"},{"line_number":41,"context_line":"            sa.BigInteger().with_variant(sa.Integer, \u0027sqlite\u0027),"},{"line_number":42,"context_line":"            nullable\u003dTrue,"},{"line_number":43,"context_line":"        ),"},{"line_number":44,"context_line":"        sa.Column("}],"source_content_type":"text/x-python","patch_set":17,"id":"05dfad31_25bb0ec3","line":41,"range":{"start_line":41,"start_character":12,"end_line":41,"end_character":62},"updated":"2026-09-01 14:05:52.000000000","message":"i wonder if we can factor that out into a constnat\n\nBIG_INT_TYPE \u003d sa.BigInteger().with_variant(sa.Integer, \u0027sqlite\u0027)\n\nwe can consider that in a followup","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"2fec6ac3408ef465574db4cd14be6b49c2263288","unresolved":true,"context_lines":[{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column("},{"line_number":40,"context_line":"            \u0027deleted\u0027,"},{"line_number":41,"context_line":"            sa.BigInteger().with_variant(sa.Integer, \u0027sqlite\u0027),"},{"line_number":42,"context_line":"            nullable\u003dTrue,"},{"line_number":43,"context_line":"        ),"},{"line_number":44,"context_line":"        sa.Column("}],"source_content_type":"text/x-python","patch_set":17,"id":"6cd6043d_9acfe6d2","line":41,"range":{"start_line":41,"start_character":12,"end_line":41,"end_character":62},"in_reply_to":"05dfad31_25bb0ec3","updated":"2026-09-01 22:04:33.000000000","message":"right, I will write this to add as a follow up","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":38,"context_line":"        sa.Column(\u0027deleted_at\u0027, sa.DateTime(), nullable\u003dTrue),"},{"line_number":39,"context_line":"        sa.Column("},{"line_number":40,"context_line":"            \u0027deleted\u0027,"},{"line_number":41,"context_line":"            sa.BigInteger().with_variant(sa.Integer, \u0027sqlite\u0027),"},{"line_number":42,"context_line":"            nullable\u003dTrue,"},{"line_number":43,"context_line":"        ),"},{"line_number":44,"context_line":"        sa.Column("}],"source_content_type":"text/x-python","patch_set":17,"id":"70a883a9_d44583d6","line":41,"range":{"start_line":41,"start_character":12,"end_line":41,"end_character":62},"in_reply_to":"6cd6043d_9acfe6d2","updated":"2026-09-02 19:41:05.000000000","message":"Acknowledged","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"ed261d0b456c94ff67087c27171f9963efca48d3","unresolved":false,"context_lines":[{"line_number":48,"context_line":"            nullable\u003dFalse,"},{"line_number":49,"context_line":"        ),"},{"line_number":50,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":51,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dFalse),"},{"line_number":52,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":53,"context_line":"        sa.Column(\u0027state\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":54,"context_line":"        sa.Column(\u0027audit_type\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":18,"id":"ad3a5dff_619026cb","line":51,"updated":"2026-09-01 22:13:19.000000000","message":"The migration and model declare audit_pipelines.name NOT NULL, but the DB API carries a comment \u0027name is nullable on AuditPipeline\u0027 plus a branch that skips the duplicate-name pre-check when name is None, and the commit message enumerates only uuid/state/audit_type as non-nullable for the new table.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Whichever contract is intended, the other side is wrong today: if name is meant nullable, the schema rejects unnamed pipelines; if required, the comment/branch are dead code and a direct dbapi create_audit_pipeline() call without \u0027name\u0027 surfaces a raw IntegrityError instead of a watcher exception.\n\n**Suggestion**:\nPick one contract and align all three places: either make name nullable in migration+model, or drop the misleading comment and None-branch and reject a missing name up front with a clean Invalid error.","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1f5986c9af76694577237d9cfa7482d88729ec7f","unresolved":false,"context_lines":[{"line_number":48,"context_line":"            nullable\u003dFalse,"},{"line_number":49,"context_line":"        ),"},{"line_number":50,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":51,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dFalse),"},{"line_number":52,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":53,"context_line":"        sa.Column(\u0027state\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":54,"context_line":"        sa.Column(\u0027audit_type\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":18,"id":"57903cc6_993ced48","line":51,"in_reply_to":"5502baa3_189e2b31","updated":"2026-09-02 15:49:22.000000000","message":"Done","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"fee626e5fe703265b4730ec6f77d9358920e8b61","unresolved":true,"context_lines":[{"line_number":48,"context_line":"            nullable\u003dFalse,"},{"line_number":49,"context_line":"        ),"},{"line_number":50,"context_line":"        sa.Column(\u0027uuid\u0027, sa.String(length\u003d36), nullable\u003dFalse),"},{"line_number":51,"context_line":"        sa.Column(\u0027name\u0027, sa.String(length\u003d63), nullable\u003dFalse),"},{"line_number":52,"context_line":"        sa.Column(\u0027execution_mode\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":53,"context_line":"        sa.Column(\u0027state\u0027, sa.String(length\u003d20), nullable\u003dFalse),"},{"line_number":54,"context_line":"        sa.Column(\u0027audit_type\u0027, sa.String(length\u003d20), nullable\u003dFalse),"}],"source_content_type":"text/x-python","patch_set":18,"id":"5502baa3_189e2b31","line":51,"in_reply_to":"ad3a5dff_619026cb","updated":"2026-09-02 11:05:47.000000000","message":"Name is not nullable anymore, I can fix the dbapi in the next PS or in a follow up.","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":102,"context_line":"            \u0027deleted\u0027,"},{"line_number":103,"context_line":"            name\u003d\u0027uniq_audit_pipeline_stages0name\u0027,"},{"line_number":104,"context_line":"        ),"},{"line_number":105,"context_line":"        sa.UniqueConstraint("},{"line_number":106,"context_line":"            \u0027position\u0027,"},{"line_number":107,"context_line":"            \u0027audit_pipeline_id\u0027,"},{"line_number":108,"context_line":"            name\u003d\u0027uniq_audit_pipeline_stages0position\u0027,"},{"line_number":109,"context_line":"        ),"},{"line_number":110,"context_line":"    )"},{"line_number":111,"context_line":""},{"line_number":112,"context_line":"    op.add_column("},{"line_number":113,"context_line":"        \u0027action_plans\u0027,"}],"source_content_type":"text/x-python","patch_set":19,"id":"4d52fe39_c99b71e3","line":110,"range":{"start_line":105,"start_character":1,"end_line":110,"end_character":5},"updated":"2026-09-02 19:41:05.000000000","message":"teim is suggetin adding deleted here\n\nhttps://minio-api.teim.app/zuul-logs/284/main/2847acd779244676b777ceb13ec50b7b/code-review/review-report.html\n\ni dont think tis corect.\n\nit woudl be correect if an only if the stages could be deleted indepnetly of hte pipeline\n\nwe could add deleted now in case we want to supprot mutating the stages in a pipeline in teh future but its not required for the lifecycle we are planning to supprot today.\n\nthese are just below the confince level requrid to report inline hence why its only in the html report","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ab5a1d6a0bed525508bb2f7006ae498af9af5151","unresolved":false,"context_lines":[{"line_number":102,"context_line":"            \u0027deleted\u0027,"},{"line_number":103,"context_line":"            name\u003d\u0027uniq_audit_pipeline_stages0name\u0027,"},{"line_number":104,"context_line":"        ),"},{"line_number":105,"context_line":"        sa.UniqueConstraint("},{"line_number":106,"context_line":"            \u0027position\u0027,"},{"line_number":107,"context_line":"            \u0027audit_pipeline_id\u0027,"},{"line_number":108,"context_line":"            name\u003d\u0027uniq_audit_pipeline_stages0position\u0027,"},{"line_number":109,"context_line":"        ),"},{"line_number":110,"context_line":"    )"},{"line_number":111,"context_line":""},{"line_number":112,"context_line":"    op.add_column("},{"line_number":113,"context_line":"        \u0027action_plans\u0027,"}],"source_content_type":"text/x-python","patch_set":19,"id":"836ba0cc_5c00e6b8","line":110,"range":{"start_line":105,"start_character":1,"end_line":110,"end_character":5},"in_reply_to":"4d52fe39_c99b71e3","updated":"2026-09-02 23:28:26.000000000","message":"yeah \"Any workflow that soft-deletes a stage and later adds another stage at the same position in the same pipeline fails with a duplicate-key error\"\nI think that we don\u0027t need this atm","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"}],"watcher/db/sqlalchemy/api.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"f2634f1771056d84ec7aa9e7a914aa97ef76a4f4","unresolved":false,"context_lines":[{"line_number":967,"context_line":"            )"},{"line_number":968,"context_line":"        except exception.ResourceNotFound:"},{"line_number":969,"context_line":"            raise exception.AuditPipelineNotFound("},{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"}],"source_content_type":"text/x-python","patch_set":1,"id":"23e07963_1a70e208","line":970,"updated":"2026-07-10 20:41:00.000000000","message":"The AuditPipeline.soft_delete() object method calls self.dbapi.soft_delete_audit_pipeline(self.uuid), passing a UUID string. Inside the DB API method, this UUID is used directly as the filter value for the audit_pipeline_id column (Integer) when looking up stages to cascade soft-delete. A UUID st...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When a pipeline is soft-deleted through the object layer (the normal production path), its stages remain with deleted_at\u003dNULL while the parent pipeline is marked as deleted. This creates orphaned, apparently-active stage records. Queries for active stages will return stages belonging to a deleted...\n\n**Priority**: Before merge\n**Why This Matters**: When a pipeline is soft-deleted through the object layer (the normal production path), its stages remain with deleted_at\u003dNULL while the parent pipeline is marked as deleted. This creates orphaned, apparently-active stage records. Queries for active stages will return stages belonging to a deleted...\n\n**Recommendation**:\nResolve the integer ID before querying for stages, mirroring destroy_audit_pipeline. For example, first query the pipeline by identity (id or uuid) to get pipeline_ref[\u0027id\u0027], then use that integer ID in the stage filter. Alternatively, filter stages by audit_pipeline_uuid using the join_fieldmap already defined in _add_audit_pipeline_stages_filters.","commit_id":"4907103b961a2c7c6bf2d69ba8d10a85cee3f914"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"bd04a63591a474648af832125a826cbca9da4909","unresolved":false,"context_lines":[{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("},{"line_number":977,"context_line":"            context, filters\u003d{\u0027audit_pipeline_id\u0027: audit_pipeline_id}"},{"line_number":978,"context_line":"        )"},{"line_number":979,"context_line":"        for stage in stages:"}],"source_content_type":"text/x-python","patch_set":2,"id":"a9a35947_e198cca6","line":976,"updated":"2026-08-03 22:31:03.000000000","message":"The soft_delete_audit_pipeline method uses the raw audit_pipeline_id parameter (which may be a UUID per the base-class docstring) to filter stages via {\u0027audit_pipeline_id\u0027: audit_pipeline_id}. Since audit_pipeline_id is an Integer column, filtering with a UUID string yields no matches, so stages...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When soft-deleting an audit pipeline via the object layer (which passes a UUID), all associated stages remain non-soft-deleted in the database, creating orphaned active stage records. This is a data-integrity issue that silently corrupts the expected cascade behavior.\n\n**Priority**: Before merge\n**Why This Matters**: When soft-deleting an audit pipeline via the object layer (which passes a UUID), all associated stages remain non-soft-deleted in the database, creating orphaned active stage records. This is a data-integrity issue that silently corrupts the expected cascade behavior.\n\n**Recommendation**:\nResolve the pipeline identity before the cascade, the same way destroy_audit_pipeline does: look up the pipeline via add_identity_filter to obtain its integer id, then use that id in the stage filter. Alternatively, call _soft_delete for the pipeline first and capture the returned row to get the integer id for the cascade.","commit_id":"c73b2dfd74ba2326e74868bd1073b3fbe2a77779"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"44b415a2bbeaf980658ee3cbfcac5185dc0a224f","unresolved":false,"context_lines":[{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":3,"id":"49f50cc3_59ea40ac","line":973,"updated":"2026-08-04 12:37:48.000000000","message":"The soft_delete_audit_pipeline method passes the raw audit_pipeline_id parameter (which may be a UUID) directly as a filter on the Integer audit_pipeline_id column of AuditPipelineStage. When a UUID is passed, the stage query returns no results and stages are left orphaned instead of being soft-d...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When AuditPipeline.soft_delete() is called through the OVO layer (the normal usage path), stages are not soft-deleted, leaving orphaned AuditPipelineStage records in the database. This breaks the intended cascade soft-delete behavior and causes data inconsistency.\n\n**Priority**: Before merge\n**Why This Matters**: When AuditPipeline.soft_delete() is called through the OVO layer (the normal usage path), stages are not soft-deleted, leaving orphaned AuditPipelineStage records in the database. This breaks the intended cascade soft-delete behavior and causes data inconsistency.\n\n**Recommendation**:\nResolve the pipeline\u0027s integer ID before querying for stages, mirroring destroy_audit_pipeline. For example: fetch the pipeline ref first via add_identity_filter, then use pipeline_ref[\u0027id\u0027] in the stage list filter.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"ff00184210cdad5a0d508202025663d52b9cbc1e","unresolved":true,"context_lines":[{"line_number":1254,"context_line":"        # ensure defaults are present for new audits"},{"line_number":1255,"context_line":"        if not values.get(\u0027uuid\u0027):"},{"line_number":1256,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":1257,"context_line":""},{"line_number":1258,"context_line":"        try:"},{"line_number":1259,"context_line":"            action_plan \u003d self._create(models.ActionPlan, values)"},{"line_number":1260,"context_line":"        except db_exc.DBDuplicateEntry:"}],"source_content_type":"text/x-python","patch_set":3,"id":"9e4d5228_bcb68b8d","line":1257,"updated":"2026-08-04 14:32:44.000000000","message":"We may also check here that we have audit_pipeline_id or strategy_id + audit_id, although if we do the validation in db, it\u0027s not strictly required.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"50d2e8219e23418f5371b2ae6e989736fe673750","unresolved":true,"context_lines":[{"line_number":1254,"context_line":"        # ensure defaults are present for new audits"},{"line_number":1255,"context_line":"        if not values.get(\u0027uuid\u0027):"},{"line_number":1256,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":1257,"context_line":""},{"line_number":1258,"context_line":"        try:"},{"line_number":1259,"context_line":"            action_plan \u003d self._create(models.ActionPlan, values)"},{"line_number":1260,"context_line":"        except db_exc.DBDuplicateEntry:"}],"source_content_type":"text/x-python","patch_set":3,"id":"f92c2d73_ac909500","line":1257,"in_reply_to":"9e4d5228_bcb68b8d","updated":"2026-08-05 15:06:32.000000000","message":"Thanks for the reminder about that, I think that we can add this logic to the OVO object instead. which will call dbapi to create it.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"de2448b66549ad762f65fbb14b25ba91eb44314e","unresolved":false,"context_lines":[{"line_number":861,"context_line":"    def create_audit_pipeline(self, values):"},{"line_number":862,"context_line":"        if not values.get(\u0027uuid\u0027):"},{"line_number":863,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":864,"context_line":""},{"line_number":865,"context_line":"        with _session_for_write() as session:"},{"line_number":866,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":867,"context_line":"            query \u003d query.filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone)"}],"source_content_type":"text/x-python","patch_set":4,"id":"58bd4d72_f810d57b","line":864,"updated":"2026-08-05 15:26:45.000000000","message":"The name uniqueness check in create_audit_pipeline uses filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone). When name is None, this matches existing rows where name IS NULL, raising AuditPipelineAlreadyExists for any subsequent unnamed pipeline. Unlike AuditTemplate (where name is non-nullable),...\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Only one AuditPipeline can ever be created without a name. Subsequent unnamed pipelines receive a misleading \u0027AuditPipelineAlreadyExists\u0027 error referencing name\u003dNone, even though the database schema allows multiple NULL names.\n\n**Suggestion**:\nSkip the name-based uniqueness check when values.get(\u0027name\u0027) is None, or use query.filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone).filter(models.AuditPipeline.name.isnot(None)) to exclude NULL-name rows from the duplicate check.","commit_id":"bfe67fcdf5893e215f0cf37741fe89ede81bfb44"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"de2448b66549ad762f65fbb14b25ba91eb44314e","unresolved":false,"context_lines":[{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":4,"id":"6c250535_66785d4d","line":973,"updated":"2026-08-05 15:26:45.000000000","message":"soft_delete_audit_pipeline passes audit_pipeline_id directly as a filter to get_audit_pipeline_stage_list for cascade deletion. Since audit_pipeline_id is in plain_fields and the DB column is Integer, passing a UUID string matches zero rows, silently skipping stage cascade deletion.\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When soft-deleting an AuditPipeline through the object layer (which always passes self.uuid), all associated AuditPipelineStage rows are left un-soft-deleted in the database, creating orphaned stage records. The DB-level test passes only because it calls with the integer ID, masking the bug.\n\n**Priority**: Before merge\n**Why This Matters**: When soft-deleting an AuditPipeline through the object layer (which always passes self.uuid), all associated AuditPipelineStage rows are left un-soft-deleted in the database, creating orphaned stage records. The DB-level test passes only because it calls with the integer ID, masking the bug.\n\n**Recommendation**:\nResolve the pipeline to get its integer ID before querying stages, following the same pattern used in destroy_audit_pipeline. Alternatively, accept a context parameter and use add_identity_filter logic, or use the join filter \u0027audit_pipeline_uuid\u0027 which is already defined in _add_audit_pipeline_stages_filters.","commit_id":"bfe67fcdf5893e215f0cf37741fe89ede81bfb44"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"d6a1eeaa50892fd7c1e282dfd8e634eb8a4e5de3","unresolved":false,"context_lines":[{"line_number":863,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":864,"context_line":""},{"line_number":865,"context_line":"        with _session_for_write() as session:"},{"line_number":866,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":867,"context_line":"            query \u003d query.filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone)"},{"line_number":868,"context_line":""},{"line_number":869,"context_line":"            if len(query.all()) \u003e 0:"}],"source_content_type":"text/x-python","patch_set":5,"id":"59920f60_65888d1a","line":866,"updated":"2026-08-05 18:57:54.000000000","message":"The create_audit_pipeline method checks for name uniqueness using filter_by(name\u003dvalues.get(\u0027name\u0027)). When name is None (which is valid since the column is nullable), SQLAlchemy generates \u0027name IS NULL\u0027, matching any existing pipeline with a NULL name and raising AuditPipelineAlreadyExists.\n\n**Severity**: HIGH | **Confidence**: 0.8\n\n**Risk**: After creating one AuditPipeline without a name, all subsequent attempts to create unnamed pipelines will fail with a false AuditPipelineAlreadyExists error. This effectively makes the nullable name field mandatory in practice.\n\n**Priority**: Before merge\n**Why This Matters**: After creating one AuditPipeline without a name, all subsequent attempts to create unnamed pipelines will fail with a false AuditPipelineAlreadyExists error. This effectively makes the nullable name field mandatory in practice.\n\n**Recommendation**:\nOnly perform the name uniqueness check when values.get(\u0027name\u0027) is not None, e.g.: if values.get(\u0027name\u0027) is not None: ...check uniqueness... This allows the DB unique constraint to handle the NULL case naturally.","commit_id":"9130d69f23dcbbb8fbc9aac60db53f62114dac47"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"d6a1eeaa50892fd7c1e282dfd8e634eb8a4e5de3","unresolved":false,"context_lines":[{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":5,"id":"66ea2367_182cdfb5","line":973,"updated":"2026-08-05 18:57:54.000000000","message":"The soft_delete_audit_pipeline DB API method receives an id-or-uuid parameter but passes it directly as a filter value for the integer audit_pipeline_id column when querying stages. When the OVO soft_delete() calls this method with self.uuid, the cascade soft-delete of stages silently does nothing.\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When soft-deleting an AuditPipeline via the object layer, its stages will not be soft-deleted, leaving orphaned stage records in the database. The cascade soft-delete logic is effectively dead code when invoked through the normal OVO path.\n\n**Priority**: Before merge\n**Why This Matters**: When soft-deleting an AuditPipeline via the object layer, its stages will not be soft-deleted, leaving orphaned stage records in the database. The cascade soft-delete logic is effectively dead code when invoked through the normal OVO path.\n\n**Recommendation**:\nResolve the audit_pipeline_id to an integer DB id before using it as a stage filter, similar to how destroy_audit_pipeline resolves pipeline_ref[\u0027id\u0027] first. Alternatively, query the pipeline by identity, then use the resolved id for the stage cascade.","commit_id":"9130d69f23dcbbb8fbc9aac60db53f62114dac47"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"cee6e6c3d2e17fd83268e6bc5d2e76856d01e228","unresolved":false,"context_lines":[{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("},{"line_number":977,"context_line":"            context, filters\u003d{\u0027audit_pipeline_id\u0027: audit_pipeline_id}"},{"line_number":978,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":6,"id":"e56bea3c_196fa1fd","line":975,"updated":"2026-08-07 19:53:37.000000000","message":"soft_delete_audit_pipeline uses the raw audit_pipeline_id parameter to filter stages by the Integer audit_pipeline_id column. When the object layer calls this method with self.uuid (a UUID string), the integer-vs-string comparison silently matches zero rows, so stages are never soft-deleted in th...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When soft-deleting an AuditPipeline through the standard object-layer path (self.uuid is passed), all associated AuditPipelineStage rows remain active (not soft-deleted), creating orphaned stage records in the database. This is a data integrity issue that silently corrupts state.\n\n**Priority**: Before merge\n**Why This Matters**: When soft-deleting an AuditPipeline through the standard object-layer path (self.uuid is passed), all associated AuditPipelineStage rows remain active (not soft-deleted), creating orphaned stage records in the database. This is a data integrity issue that silently corrupts state.\n\n**Recommendation**:\nResolve the pipeline identity to an integer ID before cascading, mirroring destroy_audit_pipeline. For example, query the AuditPipeline first via add_identity_filter to obtain pipeline_ref[\u0027id\u0027], then use that integer ID in the stage cascade filter. Alternatively, accept only integer IDs in soft_delete_audit_pipeline and resolve UUIDs in the caller.","commit_id":"10a2c0cc44f7bb619e4ecd8adb25789369d09f5c"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"3231bcd0351d5dd9b559ce54be11c021f4495731","unresolved":false,"context_lines":[{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":7,"id":"5461dcd1_ff68026e","line":973,"updated":"2026-08-12 12:42:54.000000000","message":"soft_delete_audit_pipeline passes the raw audit_pipeline_id (which may be a UUID) directly into the stage cascade filter, but the filter compares against the integer audit_pipeline_id column. When a UUID is passed, no stages are matched and the cascade is silently skipped, leaving orphaned stage...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When soft-deleting an audit pipeline by UUID, its stages are not soft-deleted, leaving orphaned stage records in the database. This causes data integrity issues and may cause errors in subsequent queries that expect stages to be deleted with their parent pipeline.\n\n**Priority**: Before merge\n**Why This Matters**: When soft-deleting an audit pipeline by UUID, its stages are not soft-deleted, leaving orphaned stage records in the database. This causes data integrity issues and may cause errors in subsequent queries that expect stages to be deleted with their parent pipeline.\n\n**Recommendation**:\nResolve the pipeline ID first before cascading, mirroring the pattern in destroy_audit_pipeline. Either call add_identity_filter to get the pipeline ref and use pipeline_ref[\u0027id\u0027], or accept only integer IDs. Alternatively, use _soft_delete on the pipeline first, then resolve and cascade to stages using the resolved integer ID.","commit_id":"789c2a9522ce4a9cf453ec24251c64a9a7b7736c"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"ef239701054fbd6315679a4b338b4d290ed8a214","unresolved":false,"context_lines":[{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":8,"id":"adad43aa_55af7ec7","line":973,"updated":"2026-08-12 22:50:00.000000000","message":"The soft_delete_audit_pipeline method passes the raw audit_pipeline_id argument (which may be a UUID string) directly as a filter to get_audit_pipeline_stage_list. The audit_pipeline_id column is Integer, so filtering by a UUID string returns no stages, meaning the cascade soft-delete silently do...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: When an AuditPipeline is soft-deleted through the object layer (which passes the UUID), its stages remain active in the database, creating orphaned stage records. This is a data-integrity issue that would affect production deployments.\n\n**Priority**: Before merge\n**Why This Matters**: When an AuditPipeline is soft-deleted through the object layer (which passes the UUID), its stages remain active in the database, creating orphaned stage records. This is a data-integrity issue that would affect production deployments.\n\n**Recommendation**:\nResolve the numeric pipeline ID before querying stages, mirroring the destroy_audit_pipeline pattern: query the pipeline first with add_identity_filter, get pipeline_ref[\u0027id\u0027], then use that numeric ID to filter and soft-delete stages.","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"d5434540657b9eab1fb2e573c09aa8a62f89b12d","unresolved":true,"context_lines":[{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":8,"id":"d93130f9_67a169c2","line":973,"in_reply_to":"adad43aa_55af7ec7","updated":"2026-08-13 14:42:46.000000000","message":"correct, going to be fixed in next PS.","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":false,"context_lines":[{"line_number":970,"context_line":"                audit_pipeline\u003daudit_pipeline_id"},{"line_number":971,"context_line":"            )"},{"line_number":972,"context_line":""},{"line_number":973,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":974,"context_line":"        # cascade: soft-delete all stages first"},{"line_number":975,"context_line":"        context \u003d context_utils.make_context()"},{"line_number":976,"context_line":"        stages \u003d self.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":8,"id":"83a61481_81916625","line":973,"in_reply_to":"d93130f9_67a169c2","updated":"2026-08-27 21:46:28.000000000","message":"Done","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"ef239701054fbd6315679a4b338b4d290ed8a214","unresolved":false,"context_lines":[{"line_number":1007,"context_line":"                audit_pipeline_stage\u003dvalues.get(\u0027uuid\u0027)"},{"line_number":1008,"context_line":"            )"},{"line_number":1009,"context_line":""},{"line_number":1010,"context_line":"    def _get_audit_pipeline_stage(self, context, fieldname, value):"},{"line_number":1011,"context_line":"        try:"},{"line_number":1012,"context_line":"            return self._get("},{"line_number":1013,"context_line":"                context,"}],"source_content_type":"text/x-python","patch_set":8,"id":"a7781e96_aaf89e65","line":1010,"updated":"2026-08-12 22:50:00.000000000","message":"The AuditPipelineStage object\u0027s get_by_uuid passes eager to _from_db_object but the DB API methods get_audit_pipeline_stage_by_id and get_audit_pipeline_stage_by_uuid always hardcode eager\u003dFalse internally, making eager loading of stage relationships non-functional.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Eager loading of related objects (audit_pipeline, goal, strategy) for AuditPipelineStage will never work at the DB query level, even when requested by callers.\n\n**Suggestion**:\nPass the eager parameter through in get_audit_pipeline_stage_by_id and get_audit_pipeline_stage_by_uuid, and add it to the _get_audit_pipeline_stage helper and the abstract method signatures in db/api.py.","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":1007,"context_line":"                audit_pipeline_stage\u003dvalues.get(\u0027uuid\u0027)"},{"line_number":1008,"context_line":"            )"},{"line_number":1009,"context_line":""},{"line_number":1010,"context_line":"    def _get_audit_pipeline_stage(self, context, fieldname, value):"},{"line_number":1011,"context_line":"        try:"},{"line_number":1012,"context_line":"            return self._get("},{"line_number":1013,"context_line":"                context,"}],"source_content_type":"text/x-python","patch_set":8,"id":"1fc51194_9180baeb","line":1010,"in_reply_to":"8487b7a9_953ff46a","updated":"2026-09-01 14:05:52.000000000","message":"Acknowledged","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"d5434540657b9eab1fb2e573c09aa8a62f89b12d","unresolved":true,"context_lines":[{"line_number":1007,"context_line":"                audit_pipeline_stage\u003dvalues.get(\u0027uuid\u0027)"},{"line_number":1008,"context_line":"            )"},{"line_number":1009,"context_line":""},{"line_number":1010,"context_line":"    def _get_audit_pipeline_stage(self, context, fieldname, value):"},{"line_number":1011,"context_line":"        try:"},{"line_number":1012,"context_line":"            return self._get("},{"line_number":1013,"context_line":"                context,"}],"source_content_type":"text/x-python","patch_set":8,"id":"8487b7a9_953ff46a","line":1010,"in_reply_to":"a7781e96_aaf89e65","updated":"2026-08-13 14:42:46.000000000","message":"right, AuditPipelineStage is the one that has relationships and shoulf pass eager parameter, going to fix in next PS","commit_id":"68722ab7b71b46b5242bd1a5615a95431a24fc40"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"19d35d1b36388f2b0e8dc2c4679d4cff20e92c38","unresolved":false,"context_lines":[{"line_number":864,"context_line":""},{"line_number":865,"context_line":"        with _session_for_write() as session:"},{"line_number":866,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":867,"context_line":"            query \u003d query.filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone)"},{"line_number":868,"context_line":""},{"line_number":869,"context_line":"            if len(query.all()) \u003e 0:"},{"line_number":870,"context_line":"                raise exception.AuditPipelineAlreadyExists("}],"source_content_type":"text/x-python","patch_set":9,"id":"938a863d_405961d0","line":867,"updated":"2026-08-13 19:50:28.000000000","message":"In create_audit_pipeline, the pre-create name uniqueness check filters by name\u003dvalues.get(\u0027name\u0027). Since name is nullable in the model, when name is None this query will match any other pipeline with a NULL name, incorrectly raising AuditPipelineAlreadyExists on the second unnamed pipeline.\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: Only one audit pipeline without a name can ever be created. The second unnamed pipeline creation will incorrectly raise AuditPipelineAlreadyExists.\n\n**Priority**: Before merge\n**Why This Matters**: Only one audit pipeline without a name can ever be created. The second unnamed pipeline creation will incorrectly raise AuditPipelineAlreadyExists.\n\n**Recommendation**:\nGuard the name uniqueness check: only run it when values.get(\u0027name\u0027) is not None, e.g. add \u0027if values.get(\"name\") is not None:\u0027 before the filter_by block.","commit_id":"496048120d81c235b93ba7a6ce067bf0e75c6aca"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"0163c81728bfb73db04245ce42e4cbefc98740c1","unresolved":false,"context_lines":[{"line_number":863,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":864,"context_line":""},{"line_number":865,"context_line":"        with _session_for_write() as session:"},{"line_number":866,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":867,"context_line":"            query \u003d query.filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone)"},{"line_number":868,"context_line":""},{"line_number":869,"context_line":"            if len(query.all()) \u003e 0:"}],"source_content_type":"text/x-python","patch_set":10,"id":"a91fb7ce_6a6d345f","line":866,"updated":"2026-08-18 13:21:29.000000000","message":"create_audit_pipeline() pre-checks name uniqueness with `query.filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone)`. The name column is nullable in both the model and the migration, and filter_by(name\u003dNone) compiles to `name IS NULL`, so after one pipeline without a name exists, every subsequent unnamed pipeline creation raises AuditPipelineAlreadyExists (with audit_pipeline\u003dNone in the message). The DB unique constraint uniq_audit_pipelines0name permits multiple NULL names, so this pre-check is stricter than the schema and blocks a state the schema explicitly supports.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: Pipelines without a name cannot be created once any unnamed pipeline exists; callers receive a misleading conflict error naming None. Either the column should be non-nullable (like AuditTemplate.name usage) or the pre-check must skip NULL names.\n\n**Suggestion**:\nOnly run the duplicate-name pre-check when `values.get(\u0027name\u0027)` is not None (the unique constraint (name, deleted) already covers named duplicates and permits NULLs), or make name required at the object/API layer if unnamed pipelines are not intended.","commit_id":"4381cddca11dce4f65074f8edba523fe04a9dc35"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"0163c81728bfb73db04245ce42e4cbefc98740c1","unresolved":false,"context_lines":[{"line_number":941,"context_line":""},{"line_number":942,"context_line":"            if is_pipeline_referenced(session, pipeline_ref[\u0027id\u0027]):"},{"line_number":943,"context_line":"                raise exception.AuditPipelineReferenced("},{"line_number":944,"context_line":"                    audit_pipeline\u003daudit_pipeline_id"},{"line_number":945,"context_line":"                )"},{"line_number":946,"context_line":""},{"line_number":947,"context_line":"            # cascade: destroy all stages before removing the pipeline"}],"source_content_type":"text/x-python","patch_set":10,"id":"7ba76917_021a531a","line":944,"updated":"2026-08-18 13:21:29.000000000","message":"destroy_audit_pipeline() cascades stage deletion by calling get_audit_pipeline_stage_list() with a freshly fabricated context from context_utils.make_context(). RequestContext.show_deleted defaults to False, so _get_model_list() filters out soft-deleted stage rows. After a pipeline has been soft-deleted (whose cascade soft-deletes all stages), those stage rows still exist with audit_pipeline_id pointing at the pipeline, so the final query.delete() on audit_pipelines violates the audit_pipeline_stages.audit_pipeline_id FK constraint and raises an unhandled sqlalchemy IntegrityError instead of destroying the pipeline.\n\n**Severity**: HIGH | **Confidence**: 0.85\n\n**Impact**: The supported hard-delete path (soft_delete then destroy, mirroring Audit/AuditTemplate lifecycle) fails with an unhandled database IntegrityError for any pipeline that was previously soft-deleted, leaving the pipeline and its stage rows in place.\n\n**Priority**: Before merge\n**Recommendation**:\nQuery the stages inside the same write session without the show_deleted filter, e.g. `session.query(models.AuditPipelineStage).filter_by(audit_pipeline_id\u003dpipeline_ref[\u0027id\u0027]).delete(synchronize_session\u003dFalse)` before `query.delete()`, or pass a context with show_deleted\u003dTrue. Add a unit test covering destroy after soft_delete.","commit_id":"4381cddca11dce4f65074f8edba523fe04a9dc35"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"46bc16e50d9b02e033c33eb0cef2a9e0ee579b07","unresolved":false,"context_lines":[{"line_number":863,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":864,"context_line":""},{"line_number":865,"context_line":"        with _session_for_write() as session:"},{"line_number":866,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":867,"context_line":"            query \u003d query.filter_by(name\u003dvalues.get(\u0027name\u0027), deleted_at\u003dNone)"},{"line_number":868,"context_line":""},{"line_number":869,"context_line":"            if len(query.all()) \u003e 0:"}],"source_content_type":"text/x-python","patch_set":12,"id":"71cbf2cb_1028cfe6","line":866,"updated":"2026-08-19 20:36:29.000000000","message":"The duplicate-name pre-check was copied from create_audit_template, where name is NOT NULL. For AuditPipeline, name is optional (models.py:259 nullable\u003dTrue, migration \u0027name\u0027 String(63) nullable\u003dTrue, OVO field nullable\u003dTrue). When values has no name, filter_by(name\u003dNone, deleted_at\u003dNone) renders \u0027WHERE name IS NULL AND deleted_at IS NULL\u0027, so the second unnamed pipeline matches the first row and raises AuditPipelineAlreadyExists. When the values dict has no \u0027name\u0027 key at all, the raise uses values[\u0027name\u0027] and crashes with KeyError instead of a proper error.\n\n**Severity**: HIGH | **Confidence**: 0.85\n\n**Impact**: Only one unnamed AuditPipeline can ever be created; every subsequent unnamed create fails with a misleading \u0027already exists\u0027 Conflict error (or an unhandled KeyError when the name key is absent), blocking a schema-supported operation for any caller using the new DB API.\n\n**Priority**: Before merge\n**Recommendation**:\nSkip the name pre-check when values.get(\u0027name\u0027) is None and use values.get(\u0027name\u0027) in both raise sites; rely on the uniq_audit_pipelines0name constraint plus the existing DBDuplicateEntry fallback for named conflicts. Add a unit test creating two pipelines with name\u003dNone.","commit_id":"bbcfe5be54863e5aa876ecbb5a9b8eb8db2e2a4f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":974,"context_line":"            )"},{"line_number":975,"context_line":""},{"line_number":976,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":977,"context_line":"        # Resolve to a concrete row first so we have the numeric PK for the"},{"line_number":978,"context_line":"        # stage cascade, regardless of whether a UUID or integer was supplied."},{"line_number":979,"context_line":"        with _session_for_read() as session:"},{"line_number":980,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":981,"context_line":"            query \u003d add_identity_filter(query, audit_pipeline_id)"}],"source_content_type":"text/x-python","patch_set":14,"id":"51525629_1e7d20d7","line":978,"range":{"start_line":977,"start_character":7,"end_line":978,"end_character":78},"updated":"2026-08-26 20:43:47.000000000","message":"we shoudl really shoudl not be caling this with a uuid some times and a db id other s\n\nthe type of a vairabel shoudl not depend on the caller so this is a code smell","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":false,"context_lines":[{"line_number":974,"context_line":"            )"},{"line_number":975,"context_line":""},{"line_number":976,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":977,"context_line":"        # Resolve to a concrete row first so we have the numeric PK for the"},{"line_number":978,"context_line":"        # stage cascade, regardless of whether a UUID or integer was supplied."},{"line_number":979,"context_line":"        with _session_for_read() as session:"},{"line_number":980,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":981,"context_line":"            query \u003d add_identity_filter(query, audit_pipeline_id)"}],"source_content_type":"text/x-python","patch_set":14,"id":"68441e46_888697f6","line":978,"range":{"start_line":977,"start_character":7,"end_line":978,"end_character":78},"in_reply_to":"28931364_9804bbfc","updated":"2026-09-02 11:45:54.000000000","message":"Acknowledged","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":974,"context_line":"            )"},{"line_number":975,"context_line":""},{"line_number":976,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":977,"context_line":"        # Resolve to a concrete row first so we have the numeric PK for the"},{"line_number":978,"context_line":"        # stage cascade, regardless of whether a UUID or integer was supplied."},{"line_number":979,"context_line":"        with _session_for_read() as session:"},{"line_number":980,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":981,"context_line":"            query \u003d add_identity_filter(query, audit_pipeline_id)"}],"source_content_type":"text/x-python","patch_set":14,"id":"a4dfa04f_8ebcd09d","line":978,"range":{"start_line":977,"start_character":7,"end_line":978,"end_character":78},"in_reply_to":"51525629_1e7d20d7","updated":"2026-08-27 12:25:09.000000000","message":"ack, makes sense, it should be a well defined interface. Most of other db api calls also consider that can be any of the 2, so it seems more safe to continue with the bad pattern? not sure if you agree","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":true,"context_lines":[{"line_number":974,"context_line":"            )"},{"line_number":975,"context_line":""},{"line_number":976,"context_line":"    def soft_delete_audit_pipeline(self, audit_pipeline_id):"},{"line_number":977,"context_line":"        # Resolve to a concrete row first so we have the numeric PK for the"},{"line_number":978,"context_line":"        # stage cascade, regardless of whether a UUID or integer was supplied."},{"line_number":979,"context_line":"        with _session_for_read() as session:"},{"line_number":980,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":981,"context_line":"            query \u003d add_identity_filter(query, audit_pipeline_id)"}],"source_content_type":"text/x-python","patch_set":14,"id":"28931364_9804bbfc","line":978,"range":{"start_line":977,"start_character":7,"end_line":978,"end_character":78},"in_reply_to":"a4dfa04f_8ebcd09d","updated":"2026-09-01 14:05:52.000000000","message":"we can do it for now but we shoudl fix it everywhere next cycle lets track this as tech debt for the ptg\n\ni think if we start adotping typeing next cycel we will find a lot of places wehre we are not proprly trackign this end to end that happen to work but are not really correct","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":980,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":981,"context_line":"            query \u003d add_identity_filter(query, audit_pipeline_id)"},{"line_number":982,"context_line":"            try:"},{"line_number":983,"context_line":"                pipeline_ref \u003d query.one()"},{"line_number":984,"context_line":"            except exc.NoResultFound:"},{"line_number":985,"context_line":"                raise exception.AuditPipelineNotFound("},{"line_number":986,"context_line":"                    audit_pipeline\u003daudit_pipeline_id"}],"source_content_type":"text/x-python","patch_set":14,"id":"9473d343_2e0ca848","line":983,"updated":"2026-08-26 20:43:47.000000000","message":"would it not be safer to do \n`audit_pipeline_id\u003dpipeline_ref[\u0027id\u0027]`\n\nhere","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":false,"context_lines":[{"line_number":980,"context_line":"            query \u003d session.query(models.AuditPipeline)"},{"line_number":981,"context_line":"            query \u003d add_identity_filter(query, audit_pipeline_id)"},{"line_number":982,"context_line":"            try:"},{"line_number":983,"context_line":"                pipeline_ref \u003d query.one()"},{"line_number":984,"context_line":"            except exc.NoResultFound:"},{"line_number":985,"context_line":"                raise exception.AuditPipelineNotFound("},{"line_number":986,"context_line":"                    audit_pipeline\u003daudit_pipeline_id"}],"source_content_type":"text/x-python","patch_set":14,"id":"feb1be75_b08b17ce","line":983,"in_reply_to":"9473d343_2e0ca848","updated":"2026-09-01 14:05:52.000000000","message":"Done this is like 997 now","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":994,"context_line":"        for stage in stages:"},{"line_number":995,"context_line":"            self.soft_delete_audit_pipeline_stage(stage[\u0027id\u0027])"},{"line_number":996,"context_line":"        try:"},{"line_number":997,"context_line":"            return self._soft_delete(models.AuditPipeline, audit_pipeline_id)"},{"line_number":998,"context_line":"        except exception.ResourceNotFound:"},{"line_number":999,"context_line":"            raise exception.AuditPipelineNotFound("},{"line_number":1000,"context_line":"                audit_pipeline\u003daudit_pipeline_id"}],"source_content_type":"text/x-python","patch_set":14,"id":"b95e939b_87e29710","line":997,"range":{"start_line":997,"start_character":59,"end_line":997,"end_character":76},"updated":"2026-08-26 20:43:47.000000000","message":"sicce this can now be either the uuid or the db id since you only used the db id in the filter above","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":false,"context_lines":[{"line_number":994,"context_line":"        for stage in stages:"},{"line_number":995,"context_line":"            self.soft_delete_audit_pipeline_stage(stage[\u0027id\u0027])"},{"line_number":996,"context_line":"        try:"},{"line_number":997,"context_line":"            return self._soft_delete(models.AuditPipeline, audit_pipeline_id)"},{"line_number":998,"context_line":"        except exception.ResourceNotFound:"},{"line_number":999,"context_line":"            raise exception.AuditPipelineNotFound("},{"line_number":1000,"context_line":"                audit_pipeline\u003daudit_pipeline_id"}],"source_content_type":"text/x-python","patch_set":14,"id":"5e565a51_c64818c6","line":997,"range":{"start_line":997,"start_character":59,"end_line":997,"end_character":76},"in_reply_to":"140c60d7_7ab13465","updated":"2026-08-27 21:46:28.000000000","message":"Done","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":994,"context_line":"        for stage in stages:"},{"line_number":995,"context_line":"            self.soft_delete_audit_pipeline_stage(stage[\u0027id\u0027])"},{"line_number":996,"context_line":"        try:"},{"line_number":997,"context_line":"            return self._soft_delete(models.AuditPipeline, audit_pipeline_id)"},{"line_number":998,"context_line":"        except exception.ResourceNotFound:"},{"line_number":999,"context_line":"            raise exception.AuditPipelineNotFound("},{"line_number":1000,"context_line":"                audit_pipeline\u003daudit_pipeline_id"}],"source_content_type":"text/x-python","patch_set":14,"id":"140c60d7_7ab13465","line":997,"range":{"start_line":997,"start_character":59,"end_line":997,"end_character":76},"in_reply_to":"b95e939b_87e29710","updated":"2026-08-27 12:25:09.000000000","message":"yeah, it can be the \u0027id\u0027 directly  here;","commit_id":"212b16679617771cfdf9695426b601e921e43315"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"5653400094b93d8dfeb2ddeee752564cb4f5a3ce","unresolved":false,"context_lines":[{"line_number":945,"context_line":"            if is_pipeline_referenced(session, pipeline_ref[\u0027id\u0027]):"},{"line_number":946,"context_line":"                raise exception.AuditPipelineReferenced("},{"line_number":947,"context_line":"                    audit_pipeline\u003daudit_pipeline_id"},{"line_number":948,"context_line":"                )"},{"line_number":949,"context_line":""},{"line_number":950,"context_line":"            # cascade: destroy all stages before removing the pipeline"},{"line_number":951,"context_line":"            context \u003d context_utils.make_context()"}],"source_content_type":"text/x-python","patch_set":15,"id":"3d35f1bb_ddc516bc","line":948,"updated":"2026-08-27 21:11:30.000000000","message":"destroy_audit_pipeline lists the pipeline\u0027s stages via get_audit_pipeline_stage_list with a freshly fabricated context (context_utils.make_context(), show_deleted\u003dFalse), so soft-deleted stage rows are excluded from the cascade. Those rows still reference audit_pipelines.id, so the subsequent query.delete() on the pipeline violates the FK constraint on MySQL/PostgreSQL and raises an unhandled DBAPIError. The common flow \u0027soft-delete a pipeline (which soft-deletes its stages) then hard-destroy it later\u0027 hits this every time. Additionally, each stage is destroyed in its own committed transaction before the pipeline row is deleted in the outer transaction, so a failure after stage deletion leaves a live pipeline with its stages permanently gone.\n\n**Severity**: HIGH | **Confidence**: 0.8\n\n**Impact**: On production databases with FK enforcement, hard-destroying a pipeline that has (or ever had) soft-deleted stages fails with an unhandled IntegrityError instead of deleting; and because stages are hard-deleted in separate committed transactions before the pipeline delete, a mid-cascade failure leaves the pipeline existing with no stages - unrecoverable data loss for that pipeline.\n\n**Priority**: Before merge\n**Recommendation**:\nDelete all stages in the same write session and transaction as the pipeline delete, without the show_deleted filter, e.g. session.query(models.AuditPipelineStage).filter_by(audit_pipeline_id\u003dpipeline_ref[\u0027id\u0027]).delete(synchronize_session\u003dFalse) before query.delete(), removing the fabricated context and the per-stage nested transactions. Add a db test that soft-deletes a stage (or the pipeline) and then destroys the pipeline.","commit_id":"67133898f50c5315e636c3718a3b21eca9e2d76b"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"17976811783aa08d9606d5d1f7637467f0c417d3","unresolved":false,"context_lines":[{"line_number":863,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":864,"context_line":""},{"line_number":865,"context_line":"        with _session_for_write() as session:"},{"line_number":866,"context_line":"            # name is nullable on AuditPipeline; skip the pre-check when no"},{"line_number":867,"context_line":"            # name is provided to allow multiple unnamed pipelines."},{"line_number":868,"context_line":"            name \u003d values.get(\u0027name\u0027)"},{"line_number":869,"context_line":"            if name is not None:"}],"source_content_type":"text/x-python","patch_set":16,"id":"34faf15b_ce5545a5","line":866,"updated":"2026-08-27 21:59:47.000000000","message":"create_audit_pipeline comments that \u0027name is nullable on AuditPipeline; skip the pre-check when no name is provided to allow multiple unnamed pipelines\u0027, and the commit message lists only uuid/state/audit_type as non-nullable. However, both the SQLAlchemy model (Column(String(63), nullable\u003dFalse)) and the migration (sa.Column(\u0027name\u0027, ..., nullable\u003dFalse)) declare name NOT NULL. A dbapi create without a name therefore skips the duplicate pre-check and fails with an unhandled IntegrityError instead of creating an unnamed pipeline, and the dead branch suggests the intended schema was nullable.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: Either the unnamed-pipeline path silently cannot work (callers hit an unwrapped oslo DB IntegrityError rather than a Watcher Conflict/Invalid), or the deployed schema is stricter than the documented design; both make the intended behavior ambiguous for the follow-up API patches in this blueprint.\n\n**Suggestion**:\nDecide the intended nullability: if unnamed pipelines are supported, make name nullable in models.py and the migration (and keep the object field nullable); otherwise remove the misleading branch and comment so the pre-check and schema agree, and let a missing name fail validation with a clear Invalid error.","commit_id":"a1210c359c0dc0a9379967664921d0aad824ee53"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":847,"context_line":"                audit_template\u003daudit_template_id"},{"line_number":848,"context_line":"            )"},{"line_number":849,"context_line":""},{"line_number":850,"context_line":"    # ### AUDIT PIPELINES ### #"},{"line_number":851,"context_line":""},{"line_number":852,"context_line":"    def get_audit_pipeline_list(self, *args, **kwargs):"},{"line_number":853,"context_line":"        return self._get_model_list("},{"line_number":854,"context_line":"            models.AuditPipeline,"}],"source_content_type":"text/x-python","patch_set":19,"id":"f6125992_5b3ea687","line":851,"range":{"start_line":850,"start_character":2,"end_line":851,"end_character":1},"updated":"2026-09-02 19:41:05.000000000","message":":) i feel like an AGENTS.md change is in our future","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":true,"context_lines":[{"line_number":916,"context_line":"            context, fieldname\u003d\"name\", value\u003daudit_pipeline_name, eager\u003deager"},{"line_number":917,"context_line":"        )"},{"line_number":918,"context_line":""},{"line_number":919,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":920,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":921,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":922,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""}],"source_content_type":"text/x-python","patch_set":19,"id":"bf603ee6_7dbd173f","line":919,"updated":"2026-09-02 19:41:05.000000000","message":"https://minio-api.teim.app/zuul-logs/284/main/2847acd779244676b777ceb13ec50b7b/code-review/review-report.html\n\nso teim thinkgs this leaks soft deleted stages.\n\n```\nDescription: The hard-delete cascade lists stages via get_audit_pipeline_stage_list \nwith a freshly constructed context_utils.make_context(), whose show_deleted \ndefaults to False, so _get_model_list filters out stages with deleted_at set. After \na pipeline has been soft-deleted (stages soft-deleted), destroy_audit_pipeline \nhard-deletes none of those stage rows and then hard-deletes the pipeline row, \nleaving orphaned soft-deleted stage rows referencing a nonexistent \naudit_pipeline_id. The FK audit_pipeline_stages.audit_pipeline_id has no ON DELETE \nbehavior, so on FK-enforcing backends (MySQL/InnoDB, PostgreSQL) the parent delete \nraises an unhandled IntegrityError. Constructing a request context inside the DB \nlayer is also a layering smell that is the root cause of the deleted-row filtering.\n\n\nImpact: On FK-enforcing databases, destroying a previously soft-deleted pipeline \nfails with an unhandled IntegrityError instead of cleaning up; on SQLite (unit \ntests, FKs off) it silently leaves orphaned stage rows with dangling FK values. \nsoft_delete_audit_pipeline\u0027s cascade also silently skips already-soft-deleted \nstages, which is benign today only because they are already soft-deleted.\n\n\nRemediation Priority: Before merge\n\nRecommendation: Do not build a request context in the DB layer. Query the stage \nrows directly inside the same write session \n(session.query(models.AuditPipelineStage).filter(audit_pipeline_id\u003d\u003dpipeline_ref.id))\nwithout the deleted_at filter, and hard-delete them before query.delete() on the \npipeline. That keeps the cascade atomic within the existing _session_for_write \nblock and covers soft-deleted rows.\n\n```","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"17cc4163a3c999ba3c9a8a925b94a51c09555bad","unresolved":true,"context_lines":[{"line_number":916,"context_line":"            context, fieldname\u003d\"name\", value\u003daudit_pipeline_name, eager\u003deager"},{"line_number":917,"context_line":"        )"},{"line_number":918,"context_line":""},{"line_number":919,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":920,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":921,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":922,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""}],"source_content_type":"text/x-python","patch_set":19,"id":"c913585d_45cff17c","line":919,"in_reply_to":"2dfe0629_43175a1a","updated":"2026-09-03 11:26:21.000000000","message":"Ack, yeah I started to look at the report yesterday instead of only the review comments here. Works for me too, and less noise","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"52feb4462554a8d657d319930de5cf7bc1c89064","unresolved":true,"context_lines":[{"line_number":916,"context_line":"            context, fieldname\u003d\"name\", value\u003daudit_pipeline_name, eager\u003deager"},{"line_number":917,"context_line":"        )"},{"line_number":918,"context_line":""},{"line_number":919,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":920,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":921,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":922,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""}],"source_content_type":"text/x-python","patch_set":19,"id":"2dfe0629_43175a1a","line":919,"in_reply_to":"475eab6a_048ca3b2","updated":"2026-09-03 00:26:07.000000000","message":"it the same ci/review system but teim report in 2 ways\n\ninlien via comment and a more verbose html report\n\nthe treshold for both are slightly diffent and may need to be tuened abit.\n\nhttps://github.com/SeanMooney/openstack-ai-style-guide/blob/master/tools/normalize_review_report.py#L26-L48\n\n```\nINLINE_THRESHOLDS \u003d {\n    \u0027critical\u0027: 0.70,\n    \u0027high\u0027: 0.75,\n    \u0027warnings\u0027: 0.80,\n    \u0027suggestions\u0027: 0.85,\n}\n\nKEEP_THRESHOLDS \u003d {\n    \u0027critical\u0027: 0.60,\n    \u0027high\u0027: 0.60,\n    \u0027warnings\u0027: 0.65,\n    \u0027suggestions\u0027: 0.70,\n}\n\nEMPTY_STATS \u003d {\n    \u0027critical\u0027: 0,\n    \u0027high\u0027: 0,\n    \u0027warnings\u0027: 0,\n    \u0027suggestions\u0027: 0,\n    \u0027total\u0027: 0,\n}\n```\n\nthe inline tresshodl for a high severity issue is 0.75 and the raw values it assigen was 0.7 so it was kept in the html veriosn but not reported inline.\n\nwe can see it elected to report it as html \n\nhttps://minio-api.teim.app/zuul-logs/284/main/2847acd779244676b777ceb13ec50b7b/code-review/review-validation.json\n\nthis was orginally how i was turning the the singal to noise ration but i might want to drop the requiremeb by 0.05-0.1 for each of the severity levels.\n\nalot of the extra output in https://zuul.teim.app/t/main/build/2847acd779244676b777ceb13ec50b7b/logs is more for debuging how/way it made the recomendation it did  but the review-report.html was how the ci orginally worked before i hooked up the inline reporting.","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ab5a1d6a0bed525508bb2f7006ae498af9af5151","unresolved":true,"context_lines":[{"line_number":916,"context_line":"            context, fieldname\u003d\"name\", value\u003daudit_pipeline_name, eager\u003deager"},{"line_number":917,"context_line":"        )"},{"line_number":918,"context_line":""},{"line_number":919,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":920,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":921,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":922,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""}],"source_content_type":"text/x-python","patch_set":19,"id":"475eab6a_048ca3b2","line":919,"in_reply_to":"bf603ee6_7dbd173f","updated":"2026-09-02 23:28:26.000000000","message":"Ack, going to solve that, it looks like a valid issue.\nBtw, are these different from teim-ci","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"b1a7a3ef1a7965ee7f64a5281232db9648c7bc60","unresolved":true,"context_lines":[{"line_number":916,"context_line":"            context, fieldname\u003d\"name\", value\u003daudit_pipeline_name, eager\u003deager"},{"line_number":917,"context_line":"        )"},{"line_number":918,"context_line":""},{"line_number":919,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":920,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":921,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":922,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""}],"source_content_type":"text/x-python","patch_set":19,"id":"94aaf7bd_8a23e532","line":919,"in_reply_to":"c913585d_45cff17c","updated":"2026-09-03 11:41:20.000000000","message":"the way i do it personally is if the inline review is clean, and i dont have any strong feedback myself that woudl need a respine i look at the html report to see if there is anythign worth bring to the review using my own judgement\n\ni.e. so i dont look at it often but i tend to look at it when im decided between +1 and +2\n\noften at that point the html report is also empty or just say \"ready to merge\" but ocationally the treshold filtered out a real issue.\n\nim planing to move the ci to glm-5.3-flash form glm-5.2 next week so ill proably drop the treshold slightly the week after once im sure the new model is workign as expected.\n\n5.3 flash is apprently much smarter then 5.2 and faster so im hoping that will speed up the review job a littel but i want ot verify that before making it more verbose by adjusting the tresholds","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":true,"context_lines":[{"line_number":942,"context_line":"                    audit_pipeline\u003daudit_pipeline_id"},{"line_number":943,"context_line":"                )"},{"line_number":944,"context_line":""},{"line_number":945,"context_line":"            # cascade: destroy all stages before removing the pipeline"},{"line_number":946,"context_line":"            context \u003d context_utils.make_context()"},{"line_number":947,"context_line":"            stages \u003d self.get_audit_pipeline_stage_list("},{"line_number":948,"context_line":"                context, filters\u003d{\u0027audit_pipeline_id\u0027: pipeline_ref[\u0027id\u0027]}"},{"line_number":949,"context_line":"            )"},{"line_number":950,"context_line":"            for stage in stages:"},{"line_number":951,"context_line":"                self.destroy_audit_pipeline_stage(stage[\u0027id\u0027])"},{"line_number":952,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"55790263_ffa4a973","line":949,"range":{"start_line":945,"start_character":12,"end_line":949,"end_character":13},"updated":"2026-09-02 19:41:05.000000000","message":"so its saying do this\n`stages \u003d session.query(models.AuditPipelineStage).filter(audit_pipeline_id\u003d\u003dpipeline_ref.id).All()`\n\nor soemthign like that\n\nalternitivel we woudl need to set show_deelte on teh context object above.\n\n\n\n```suggestion\n            # cascade: destroy all stages before removing the pipeline\n            context \u003d context.make_context(show_deleted\u003dTrue)\n            stages \u003d self.get_audit_pipeline_stage_list(\n                context, filters\u003d{\u0027audit_pipeline_id\u0027: pipeline_ref[\u0027id\u0027]}\n            )\n```\n\nlike in purge\n\nhttps://github.com/openstack/watcher/blob/8599261212fa5eab6444a1b2ea9d55af6e6d3207/watcher/db/purge.py#L128\n\n\nim kind o funsuer beacse for the action plans this is done at the obejct level which feels more corect to me\n\n\nhttps://github.com/openstack/watcher/blob/8599261212fa5eab6444a1b2ea9d55af6e6d3207/watcher/objects/action_plan.py#L287-L289\n\nwe calls destoy on each fo the related_efficacy_indicators\n\nso at teh object layer we shoudl be doing the same for each of the stages\n\nand then the actul delet eshoudl be just like destroy_action_plan\n\nhttps://github.com/openstack/watcher/blob/master/watcher/db/sqlalchemy/api.py#L1004-L1027\n \n \n we check if the pipelien is still refenced check both for action plans and stages in   is_pipeline_referenced and rasiign if that is true.\n \n \n so i think the actual bug is that you shoudl not eb doing \n \n ```\n            # cascade: destroy all stages before removing the pipeline\n            context \u003d context_utils.make_context()\n            stages \u003d self.get_audit_pipeline_stage_list(\n                context, filters\u003d{\u0027audit_pipeline_id\u0027: pipeline_ref[\u0027id\u0027]}\n            )\n            for stage in stages:\n                self.destroy_audit_pipeline_stage(stage[\u0027id\u0027])\n```\n\nin the db layer at all it shoudl be up one level in the obejct\n\n-1 becasue i think this is a real logic bug","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1fe788d3b6d87e450b6050f4ca0a6e68a0424606","unresolved":false,"context_lines":[{"line_number":942,"context_line":"                    audit_pipeline\u003daudit_pipeline_id"},{"line_number":943,"context_line":"                )"},{"line_number":944,"context_line":""},{"line_number":945,"context_line":"            # cascade: destroy all stages before removing the pipeline"},{"line_number":946,"context_line":"            context \u003d context_utils.make_context()"},{"line_number":947,"context_line":"            stages \u003d self.get_audit_pipeline_stage_list("},{"line_number":948,"context_line":"                context, filters\u003d{\u0027audit_pipeline_id\u0027: pipeline_ref[\u0027id\u0027]}"},{"line_number":949,"context_line":"            )"},{"line_number":950,"context_line":"            for stage in stages:"},{"line_number":951,"context_line":"                self.destroy_audit_pipeline_stage(stage[\u0027id\u0027])"},{"line_number":952,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"736504b5_b7baef36","line":949,"range":{"start_line":945,"start_character":12,"end_line":949,"end_character":13},"in_reply_to":"14119ea8_d6b6447b","updated":"2026-09-03 10:50:39.000000000","message":"Done","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ab5a1d6a0bed525508bb2f7006ae498af9af5151","unresolved":true,"context_lines":[{"line_number":942,"context_line":"                    audit_pipeline\u003daudit_pipeline_id"},{"line_number":943,"context_line":"                )"},{"line_number":944,"context_line":""},{"line_number":945,"context_line":"            # cascade: destroy all stages before removing the pipeline"},{"line_number":946,"context_line":"            context \u003d context_utils.make_context()"},{"line_number":947,"context_line":"            stages \u003d self.get_audit_pipeline_stage_list("},{"line_number":948,"context_line":"                context, filters\u003d{\u0027audit_pipeline_id\u0027: pipeline_ref[\u0027id\u0027]}"},{"line_number":949,"context_line":"            )"},{"line_number":950,"context_line":"            for stage in stages:"},{"line_number":951,"context_line":"                self.destroy_audit_pipeline_stage(stage[\u0027id\u0027])"},{"line_number":952,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"14119ea8_d6b6447b","line":949,"range":{"start_line":945,"start_character":12,"end_line":949,"end_character":13},"in_reply_to":"55790263_ffa4a973","updated":"2026-09-02 23:28:26.000000000","message":"I agree that this logic should not be in db layer, there is other examples of logics spread here already which is not correct. In the same way that we move the Action Plan validation (audit_id/strategy_id !\u003d Null and audit_pipeline_id !\u003d Null) to object level.","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"39647e1522cfcc7c37d88440f94ea56c54e45371","unresolved":false,"context_lines":[{"line_number":1006,"context_line":"        )"},{"line_number":1007,"context_line":""},{"line_number":1008,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":1009,"context_line":"    def create_audit_pipeline_stage(self, values):"},{"line_number":1010,"context_line":"        if not values.get(\u0027uuid\u0027):"},{"line_number":1011,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":1012,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"f40aa1c6_65d7c683","line":1009,"updated":"2026-09-02 16:01:45.000000000","message":"create_audit_pipeline_stage maps every db_exc.DBDuplicateEntry to AuditPipelineStageAlreadyExists, whose message says \u0027An audit_pipeline_stage with UUID ... already exists\u0027. But the table has three unique constraints (uuid, name+audit_pipeline_id+deleted, position+audit_pipeline_id), so a name or position collision raises an error message that points the user at the wrong column.\n\n**Severity**: SUGGESTION | **Confidence**: 0.85\n\n**Impact**: Users hitting a duplicate stage name or position inside a pipeline get a misleading error mentioning a UUID that was freshly generated, making API-level diagnosis and error translation wrong once exposed.\n\n**Recommendation**:\nInspect the DBDuplicateEntry columns (e.g. e.columns) or pre-check name/position like create_audit_pipeline does for name, and raise a distinct, accurate message for name/position conflicts (e.g. reuse the AuditPipelineStageAlreadyExists pattern with a name/position-specific exception).","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":1006,"context_line":"        )"},{"line_number":1007,"context_line":""},{"line_number":1008,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":1009,"context_line":"    def create_audit_pipeline_stage(self, values):"},{"line_number":1010,"context_line":"        if not values.get(\u0027uuid\u0027):"},{"line_number":1011,"context_line":"            values[\u0027uuid\u0027] \u003d utils.generate_uuid()"},{"line_number":1012,"context_line":""}],"source_content_type":"text/x-python","patch_set":19,"id":"7d4d012a_00ecc912","line":1009,"in_reply_to":"f40aa1c6_65d7c683","updated":"2026-09-02 19:41:05.000000000","message":"we could improve this but i think that fine for now\n\nduplicate names/position shoudl be caugt at the api level not the db level as part fo request pramater validation not here.","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"4032a651e33edbfabcca79433e783625fa86c06b","unresolved":false,"context_lines":[{"line_number":916,"context_line":"        )"},{"line_number":917,"context_line":""},{"line_number":918,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":919,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":920,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":921,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""},{"line_number":922,"context_line":"            query \u003d session.query(models.ActionPlan)"}],"source_content_type":"text/x-python","patch_set":20,"id":"613ae20d_e6306bf9","line":919,"updated":"2026-10-07 13:01:25.000000000","message":"The new BaseConnection abstract docstrings and the commit message both state that the DB API cascades: \u0027Destroy an audit pipeline and its stages.\u0027 (db/api.py:444), \u0027Soft delete an audit pipeline and its stages.\u0027 (db/api.py:462), and \u0027destroy (cascades to stages), soft-delete (cascades soft-delete to stages)\u0027 in the commit message. The SQLAlchemy implementation does neither: destroy_audit_pipeline checks action-plan references and then deletes only the pipeline row via query.delete(), and soft_delete_audit_pipeline calls the single-row _soft_delete helper. Only the versioned object layer (AuditPipeline.destroy/soft_delete) performs the stage cascade, which the new DB unit test comment itself acknowledges.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: The DB API layer\u0027s documented contract is not met, so future direct DB API callers or alternate backend implementers following the docstring will produce orphaned stage rows or raw IntegrityErrors instead of the documented cascade, and the commit history documents behavior that does not exist at that layer.\n\n**Suggestion**:\nEither implement the cascade inside destroy_audit_pipeline/soft_delete_audit_pipeline (delete or soft-delete the pipeline\u0027s stages within the same write session), or correct the abstract docstrings in watcher/db/api.py and the commit message to state that stage cascading is the caller\u0027s (object layer\u0027s) responsibility, as the DB unit test comment already implies.","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"da21fc98e629b45c71343e6ed19614b91556c991","unresolved":false,"context_lines":[{"line_number":916,"context_line":"        )"},{"line_number":917,"context_line":""},{"line_number":918,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":919,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":920,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":921,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""},{"line_number":922,"context_line":"            query \u003d session.query(models.ActionPlan)"}],"source_content_type":"text/x-python","patch_set":20,"id":"b971b84e_b0bf6166","line":919,"updated":"2026-09-02 23:39:25.000000000","message":"destroy_audit_pipeline() only checks action_plan references and then runs a bulk `query.delete()` on the audit_pipelines row. The audit_pipeline_stages.audit_pipeline_id FK has no ON DELETE CASCADE (migration line ~101) and the model\u0027s `stages` relationship defines no delete cascade, and a bulk DELETE bypasses ORM cascades anyway. On FK-enforcing backends (MySQL, PostgreSQL), destroying a pipeline that has any stage row (live or soft-deleted) fails with an unhandled DB IntegrityError instead of destroying the pipeline and its stages as the docstring and abstract API contract promise. The object layer works around this by hard-deleting stages first (AuditPipeline.destroy()), but the DB API in watcher/db/api.py is a public interface whose declared contract (\u0027Destroy an audit pipeline and its stages\u0027) is not honored, and no test exercises dbapi destroy with stages present (SQLite tests do not enforce FKs by default).\n\n**Severity**: HIGH | **Confidence**: 0.8\n\n**Impact**: Any direct consumer of the DB API (watcher services and future API/planner code calling dbapi.destroy_audit_pipeline) gets an unhandled IntegrityError 1451/FK violation whenever a stage row exists, so the supported \u0027destroy pipeline with stages\u0027 operation cannot complete. The commit message\u0027s claim that destroy \u0027cascades to stages\u0027 at the DB API level is not implemented.\n\n**Priority**: Before merge\n**Recommendation**:\nInside the same write session, delete stage rows before the pipeline row (e.g. session.query(models.AuditPipelineStage).filter_by(audit_pipeline_id\u003dpipeline_ref[\u0027id\u0027]).delete()), or declare cascade (relationship cascade\u003d\u0027all, delete-orphan\u0027 with session.delete(), or FK ondelete\u003d\u0027CASCADE\u0027 in the migration) - the explicit delete is the smallest safe fix. Add a DB test that creates stages (including a soft-deleted one) and then calls destroy_audit_pipeline.","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"17cc4163a3c999ba3c9a8a925b94a51c09555bad","unresolved":true,"context_lines":[{"line_number":916,"context_line":"        )"},{"line_number":917,"context_line":""},{"line_number":918,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":919,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":920,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":921,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""},{"line_number":922,"context_line":"            query \u003d session.query(models.ActionPlan)"}],"source_content_type":"text/x-python","patch_set":20,"id":"9a54bf93_a9d3d4cb","line":919,"in_reply_to":"5fb570aa_95840f37","updated":"2026-09-03 11:26:21.000000000","message":"agree, we can improve the check (it was not added before because the db api was deleting all stages) -  but we should check if a stage exists and fail here. We can address in a follow up, noted here.","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1fe788d3b6d87e450b6050f4ca0a6e68a0424606","unresolved":true,"context_lines":[{"line_number":916,"context_line":"        )"},{"line_number":917,"context_line":""},{"line_number":918,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":919,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":920,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":921,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""},{"line_number":922,"context_line":"            query \u003d session.query(models.ActionPlan)"}],"source_content_type":"text/x-python","patch_set":20,"id":"be6c12b9_252d5cae","line":919,"in_reply_to":"85d8a40c_dd8c5739","updated":"2026-09-03 10:50:39.000000000","message":"no the finding is correct\n\nthe audit pipeline need to check both for action plans and stages the refence it\n\nis_pipeline_referenced is a safety check to prevent use leave the db in a currpt state with partly deleted pipelines\n\nnow the new code shoudl not get to that sate as you said its now done in the object but we shoudl be checkign that there are no stages left refecnign the pipeline as a safty messuer\n\n\nthe coudl shoudl be correct so i think we can hardent this in a followup.\n\nif we didn\u0027t have the safety check for stale action plans refences it would not have commented here i think.","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"1099f79798608f7d0d690c3f931166ab2a5170ff","unresolved":true,"context_lines":[{"line_number":916,"context_line":"        )"},{"line_number":917,"context_line":""},{"line_number":918,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":919,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":920,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":921,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""},{"line_number":922,"context_line":"            query \u003d session.query(models.ActionPlan)"}],"source_content_type":"text/x-python","patch_set":20,"id":"85d8a40c_dd8c5739","line":919,"in_reply_to":"b971b84e_b0bf6166","updated":"2026-09-03 10:06:37.000000000","message":"IIUC this is not currently a problem anymore, since the destroy method on the object level of the audit pipeline will destroy all the stages. So what\u0027s causing this comment is the docstring of `destroy_audit_pipeline` in watcher/db/api.py which should be updated since it does not touch the stages anymore, is that correct?","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"ad03466c2f13abb260729968e5ea53219954e131","unresolved":true,"context_lines":[{"line_number":916,"context_line":"        )"},{"line_number":917,"context_line":""},{"line_number":918,"context_line":"    @oslo_db_api.retry_on_deadlock"},{"line_number":919,"context_line":"    def destroy_audit_pipeline(self, audit_pipeline_id):"},{"line_number":920,"context_line":"        def is_pipeline_referenced(session, pipeline_id):"},{"line_number":921,"context_line":"            \"\"\"Check whether the pipeline is referenced by action_plan(s).\"\"\""},{"line_number":922,"context_line":"            query \u003d session.query(models.ActionPlan)"}],"source_content_type":"text/x-python","patch_set":20,"id":"5fb570aa_95840f37","line":919,"in_reply_to":"be6c12b9_252d5cae","updated":"2026-09-03 10:54:54.000000000","message":"ack, thanks, I agree this can be adressed in a follow-up","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"}],"watcher/db/sqlalchemy/models.py":[{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"ff00184210cdad5a0d508202025663d52b9cbc1e","unresolved":true,"context_lines":[{"line_number":217,"context_line":"    audit_pipeline \u003d orm.relationship("},{"line_number":218,"context_line":"        \u0027AuditPipeline\u0027, foreign_keys\u003daudit_pipeline_id, lazy\u003dNone"},{"line_number":219,"context_line":"    )"},{"line_number":220,"context_line":""},{"line_number":221,"context_line":""},{"line_number":222,"context_line":"class Action(Base):"},{"line_number":223,"context_line":"    \"\"\"Represents an action.\"\"\""}],"source_content_type":"text/x-python","patch_set":3,"id":"d75a1b36_67821759","line":220,"updated":"2026-08-04 14:32:44.000000000","message":"Should we also add a constraint to ActionPlan so that the `audit_pipeline_id IS NOT NULL OR (audit_id IS NOT NULL AND strategy_id IS NOT NULL)`","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"06813565f69cd6c3fe654cc2b90c1510c3bd21dd","unresolved":true,"context_lines":[{"line_number":217,"context_line":"    audit_pipeline \u003d orm.relationship("},{"line_number":218,"context_line":"        \u0027AuditPipeline\u0027, foreign_keys\u003daudit_pipeline_id, lazy\u003dNone"},{"line_number":219,"context_line":"    )"},{"line_number":220,"context_line":""},{"line_number":221,"context_line":""},{"line_number":222,"context_line":"class Action(Base):"},{"line_number":223,"context_line":"    \"\"\"Represents an action.\"\"\""}],"source_content_type":"text/x-python","patch_set":3,"id":"19374253_5b69ca7e","line":220,"in_reply_to":"39ab8caa_f83b2aa1","updated":"2026-08-28 15:07:39.000000000","message":"So in the end was added here: https://review.opendev.org/c/openstack/watcher/+/996849/14/watcher/objects/action_plan.py#182\nlet me know if we can resolve this thread.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"50d2e8219e23418f5371b2ae6e989736fe673750","unresolved":true,"context_lines":[{"line_number":217,"context_line":"    audit_pipeline \u003d orm.relationship("},{"line_number":218,"context_line":"        \u0027AuditPipeline\u0027, foreign_keys\u003daudit_pipeline_id, lazy\u003dNone"},{"line_number":219,"context_line":"    )"},{"line_number":220,"context_line":""},{"line_number":221,"context_line":""},{"line_number":222,"context_line":"class Action(Base):"},{"line_number":223,"context_line":"    \"\"\"Represents an action.\"\"\""}],"source_content_type":"text/x-python","patch_set":3,"id":"39ab8caa_f83b2aa1","line":220,"in_reply_to":"d75a1b36_67821759","updated":"2026-08-05 15:06:32.000000000","message":"I think that works better in the OVO object, which should be the interface to create these objects. I will push an update and let me know what do you think","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"ff00184210cdad5a0d508202025663d52b9cbc1e","unresolved":true,"context_lines":[{"line_number":257,"context_line":"    uuid \u003d Column(String(36), nullable\u003dFalse)"},{"line_number":258,"context_line":"    name \u003d Column(String(63), nullable\u003dTrue)"},{"line_number":259,"context_line":"    execution_mode \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":260,"context_line":"    state \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":261,"context_line":"    audit_type \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":262,"context_line":"    auto_trigger \u003d Column(Boolean, nullable\u003dFalse, default\u003dFalse)"},{"line_number":263,"context_line":"    hostname \u003d Column(String(255), nullable\u003dTrue)"}],"source_content_type":"text/x-python","patch_set":3,"id":"3ee44f47_caf643d1","line":260,"updated":"2026-08-04 14:32:44.000000000","message":"audits and actionplans have state as nullable, so that initial state can be unset and moved to NEW as different action. I understand you want to set NEW state in creation.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"50d2e8219e23418f5371b2ae6e989736fe673750","unresolved":true,"context_lines":[{"line_number":257,"context_line":"    uuid \u003d Column(String(36), nullable\u003dFalse)"},{"line_number":258,"context_line":"    name \u003d Column(String(63), nullable\u003dTrue)"},{"line_number":259,"context_line":"    execution_mode \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":260,"context_line":"    state \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":261,"context_line":"    audit_type \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":262,"context_line":"    auto_trigger \u003d Column(Boolean, nullable\u003dFalse, default\u003dFalse)"},{"line_number":263,"context_line":"    hostname \u003d Column(String(255), nullable\u003dTrue)"}],"source_content_type":"text/x-python","patch_set":3,"id":"c17989f5_d511f4ca","line":260,"in_reply_to":"3ee44f47_caf643d1","updated":"2026-08-05 15:06:32.000000000","message":"I don\u0027t see why this field would be Nullable tbh, since these objects should always have a state. Audits should always start with PENDING (there is validation in the dbapi level for that), the same is valid for AuditPipeline. So I think that is more correct to set as nullable\u003dFalse here, wdyt? Thanks for raising it.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":true,"context_lines":[{"line_number":257,"context_line":"    uuid \u003d Column(String(36), nullable\u003dFalse)"},{"line_number":258,"context_line":"    name \u003d Column(String(63), nullable\u003dTrue)"},{"line_number":259,"context_line":"    execution_mode \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":260,"context_line":"    state \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":261,"context_line":"    audit_type \u003d Column(String(20), nullable\u003dFalse)"},{"line_number":262,"context_line":"    auto_trigger \u003d Column(Boolean, nullable\u003dFalse, default\u003dFalse)"},{"line_number":263,"context_line":"    hostname \u003d Column(String(255), nullable\u003dTrue)"}],"source_content_type":"text/x-python","patch_set":3,"id":"df4d0f60_08654654","line":260,"in_reply_to":"c17989f5_d511f4ca","updated":"2026-09-02 11:45:54.000000000","message":"that feel like more of a but in the audit and action plan then somethi8ng we shoudl continue doing os i agree we shoudl not make this nullable","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"17976811783aa08d9606d5d1f7637467f0c417d3","unresolved":false,"context_lines":[{"line_number":276,"context_line":"    \"\"\"Represents one stage in an audit pipeline.\"\"\""},{"line_number":277,"context_line":""},{"line_number":278,"context_line":"    __tablename__ \u003d \u0027audit_pipeline_stages\u0027"},{"line_number":279,"context_line":"    __table_args__ \u003d ("},{"line_number":280,"context_line":"        UniqueConstraint(\u0027uuid\u0027, name\u003d\u0027uniq_audit_pipeline_stages0uuid\u0027),"},{"line_number":281,"context_line":"        table_args(),"},{"line_number":282,"context_line":"    )"}],"source_content_type":"text/x-python","patch_set":16,"id":"b6384f0e_800d0e25","line":279,"updated":"2026-08-27 21:59:47.000000000","message":"The alembic migration creates audit_pipeline_stages with two additional unique constraints, uniq_audit_pipeline_stages0name (name, deleted) and uniq_audit_pipeline_stages0position (position, audit_pipeline_id), but the ORM model AuditPipelineStage in watcher/db/sqlalchemy/models.py declares only uniq_audit_pipeline_stages0uuid. Watcher\u0027s unit/db tests build the schema from the model metadata, so the constraints enforced in production deployments are invisible to the test suite and the model can silently drift from the deployed schema. A unit test that inserts two stages with the same position/name for one pipeline passes against the model schema but fails in production with an unexpected integrity error.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: Behavior differs between test/dev schema (built from models) and deployed schema (built from migrations): duplicate stage names or positions are accepted by tests and unit DB flows but rejected by production databases, producing untested failure paths (and the misleading exception of CF-002). Future schema autogeneration or drift detection will also produce spurious diffs.\n\n**Suggestion**:\nAdd UniqueConstraint(\u0027name\u0027, \u0027deleted\u0027, name\u003d\u0027uniq_audit_pipeline_stages0name\u0027) and UniqueConstraint(\u0027position\u0027, \u0027audit_pipeline_id\u0027, name\u003d\u0027uniq_audit_pipeline_stages0position\u0027) to AuditPipelineStage.__table_args__ in models.py so the model matches the migration, or drop them from the migration if stage names/positions are not meant to be unique; then add a test covering the chosen behavior.","commit_id":"a1210c359c0dc0a9379967664921d0aad824ee53"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"4032a651e33edbfabcca79433e783625fa86c06b","unresolved":false,"context_lines":[{"line_number":292,"context_line":"            name\u003d\u0027uniq_audit_pipeline_stages0name\u0027,"},{"line_number":293,"context_line":"        ),"},{"line_number":294,"context_line":"        UniqueConstraint("},{"line_number":295,"context_line":"            \u0027position\u0027,"},{"line_number":296,"context_line":"            \u0027audit_pipeline_id\u0027,"},{"line_number":297,"context_line":"            name\u003d\u0027uniq_audit_pipeline_stages0position\u0027,"},{"line_number":298,"context_line":"        ),"}],"source_content_type":"text/x-python","patch_set":20,"id":"baae5cd7_e8b5634a","line":295,"updated":"2026-10-07 13:01:25.000000000","message":"The audit_pipeline_stages position uniqueness constraint uniq_audit_pipeline_stages0position covers (position, audit_pipeline_id) without the \u0027deleted\u0027 column, unlike the sibling name constraint uniq_audit_pipeline_stages0name which includes \u0027deleted\u0027. Soft-deleting a stage (exposed as AuditPipelineStage.soft_delete) keeps its position and audit_pipeline_id values in the row (deleted becomes the row id), so no new or updated stage can ever use that position in the same pipeline again. When creation subsequently hits the constraint, create_audit_pipeline_stage translates DBDuplicateEntry into AuditPipelineStageAlreadyExists keyed on the new stage\u0027s UUID, which points at the wrong cause.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: A supported operation sequence (soft-delete a stage, then create or move another stage into that position) fails with DBDuplicateEntry surfaced as a misleading \u0027stage with UUID \u003cnew-uuid\u003e already exists\u0027 error; the occupied position can only be recovered by destroying the whole pipeline, since destroyed rows are removed but soft-deleted rows are never cleared.\n\n**Suggestion**:\nAdd \u0027deleted\u0027 to the position unique constraint in both watcher/db/sqlalchemy/models.py and the 4194bfa10c1a migration so positions of soft-deleted stages become reusable, matching the name constraint; optionally raise a clearer exception (e.g., naming the conflicting position) when the constraint is hit.","commit_id":"7cbec7cf6260cf8a5e8832e59f1dfa2ccabb236c"}],"watcher/objects/action_plan.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":true,"context_lines":[{"line_number":179,"context_line":"        else:"},{"line_number":180,"context_line":"            self._old_state, self._state \u003d self._state, value"},{"line_number":181,"context_line":""},{"line_number":182,"context_line":"    def validate(self):"},{"line_number":183,"context_line":"        has_audit \u003d ("},{"line_number":184,"context_line":"            self.obj_attr_is_set(\u0027audit_id\u0027) and self.audit_id is not None"},{"line_number":185,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":14,"id":"7630db26_6d76eaa4","line":182,"range":{"start_line":182,"start_character":8,"end_line":182,"end_character":16},"updated":"2026-09-01 14:05:52.000000000","message":"i guess callitn this at create is ok we cuold codnier if it shoudl be __post_init__ later\n\nit really depend on when we want the enveritn ot be enforced\n\nwhen the object is constucted or when it is saved to the db.\nright now your doing the enfoce before we save/create it to the db whihc is valid\n\nensuring we alwasy have a valid obejct when we cosntruct it may force some patteres that we dont nessiarly want depending on existin usage or test patterns\n\nbut i think __post_init__ likely would be valid to call validate on as well.\n\nwe can discuss that in a followup","commit_id":"212b16679617771cfdf9695426b601e921e43315"}],"watcher/objects/audit_pipeline.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"f2634f1771056d84ec7aa9e7a914aa97ef76a4f4","unresolved":false,"context_lines":[{"line_number":219,"context_line":""},{"line_number":220,"context_line":"    @classmethod"},{"line_number":221,"context_line":"    @base.remotable"},{"line_number":222,"context_line":"    def get(cls, context, stage_id, eager\u003dFalse):"},{"line_number":223,"context_line":"        \"\"\"Find a pipeline stage based on its id or uuid.\"\"\""},{"line_number":224,"context_line":"        if utils.is_int_like(stage_id):"},{"line_number":225,"context_line":"            return cls.get_by_id(context, stage_id)"}],"source_content_type":"text/x-python","patch_set":1,"id":"85794306_e56b6183","line":222,"updated":"2026-07-10 20:41:00.000000000","message":"The AuditPipelineStage.get() classmethod includes eager\u003dFalse in its signature but never passes the value to get_by_id or get_by_uuid. The underlying DB API methods _get_audit_pipeline_stage and get_audit_pipeline_stage_by_id/by_uuid also lack an eager parameter and hardcode eager\u003dFalse. Callers...\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: A caller calling AuditPipelineStage.get(context, stage_id, eager\u003dTrue) will not receive eagerly-loaded relationship objects (audit_pipeline, goal, strategy). The API contract is silently violated, which can cause N+1 query problems or unexpected None values for relationship fields at runtime.\n\n**Recommendation**:\nEither thread the eager parameter through get_by_id, get_by_uuid, and the DB API methods (adding eager support to _get_audit_pipeline_stage, get_audit_pipeline_stage_by_id, get_audit_pipeline_stage_by_uuid, and the BaseConnection abstract methods), or remove the eager parameter from the get() method signature to avoid misleading callers.","commit_id":"4907103b961a2c7c6bf2d69ba8d10a85cee3f914"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"44b415a2bbeaf980658ee3cbfcac5185dc0a224f","unresolved":false,"context_lines":[{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"}],"source_content_type":"text/x-python","patch_set":3,"id":"7930145f_b513adcc","line":70,"updated":"2026-08-04 12:37:48.000000000","message":"The AuditPipeline OVO defines auto_trigger as BooleanField(default\u003dTrue), but the SQLAlchemy model sets default\u003dFalse and the DB API create_audit_pipeline forces auto_trigger to False when not explicitly provided. The existing Audit OVO uses BooleanField() without a default for the same field, ma...\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Consumers reading auto_trigger from an unpersisted AuditPipeline OVO will see True, but the value stored in the database after create() will be False if not explicitly set. This inconsistency can cause confusion and unexpected behavior.\n\n**Recommendation**:\nChange the OVO field to BooleanField() without a default (matching the Audit OVO pattern), or change it to BooleanField(default\u003dFalse) to align with the model and DB API defaults.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":16312,"name":"Alfredo Moralejo","email":"amoralej@redhat.com","username":"amoralej"},"change_message_id":"ff00184210cdad5a0d508202025663d52b9cbc1e","unresolved":true,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"91fc46d8_6ecc2589","line":72,"updated":"2026-08-04 14:32:44.000000000","message":"IIUC, the API the AuditPipeline has field `stages` with the list of stages, should we also add it to the object?","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"6d93042dcb7682edd12c20c8c7d36b26de5a247b","unresolved":true,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"9b6692c3_58329b5c","line":72,"in_reply_to":"1af2fe32_c01b8e93","updated":"2026-08-27 12:25:09.000000000","message":"- We don\u0027t expose state as top level api in this first imp\n- We can include stages here yeah, it is most of the time fetching them and adding in most of the responses.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"f0f93980_589420c9","line":72,"in_reply_to":"1c66685c_d013329f","updated":"2026-09-01 16:27:25.000000000","message":"AuditPipelineStageList isnt actully needed for watcher because all the proecess have direct db access\n\nwe need it for nova because of the ovo indreiction api and even thne it might technialy be ok wiht out it so sorry i missed you directly used \n\novo_fields.ListOfObjectsField that fine for our current usage.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"50d2e8219e23418f5371b2ae6e989736fe673750","unresolved":true,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"ad534248_9854ef9c","line":72,"in_reply_to":"91fc46d8_6ecc2589","updated":"2026-08-05 15:06:32.000000000","message":"They are not always needed, even in some API calls like a simple list() of Audit Pipelines. When stages are needed, we fetch them using list() directly in the code, providing the audit_pipeline_id as filter. It seems that is not common to have OVO including child\u0027s collections. This patch is not adding stages to AuditPipeline model too, only Stages have a reference to audit_pipeline_id. Do you see an issue with that approach?","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ee9a489fe8ed9d9bad0edf072fd47df9c9227b8","unresolved":true,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"1c66685c_d013329f","line":72,"in_reply_to":"9651dce3_f622e073","updated":"2026-09-01 14:05:52.000000000","message":"ack but you didn tadd the field as a nullable filed which is what i actully was suggesting we coudl do this in 1.1 but i think  we are missign a AuditPipelineStageList type and stages here.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"a651c69a78622358f66d56366825ea0c73502654","unresolved":true,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"9651dce3_f622e073","line":72,"in_reply_to":"9b6692c3_58329b5c","updated":"2026-08-27 21:46:28.000000000","message":"OK, so I added stages to AuditPipeline as proposed, and replace the parts in API (other patch in chain) that were fetch stages, so now AuditPipeline can eager load stage and its goal/strategy info.","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4e223af996c22ab9244430afa5dbcfc3dde9cbdb","unresolved":true,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"1af2fe32_c01b8e93","line":72,"in_reply_to":"ad534248_9854ef9c","updated":"2026-08-26 20:43:47.000000000","message":"it depedns\n\nif the stages are a top level api endpoint then we soul no need to include them but if the pipelien is the only top level api resouce then we shoudl include them\n\ni think we shoudl not expose stages at teh top level\n\nthe notificaion pipeline will include ti per the spec\n\nhttps://opendev.org/openstack/watcher-specs/src/branch/master/specs/2026.2/approved/audit-pipeline.rst?display\u003dsource#L732\n\n\ni woudl honestly include ti too.\n\nwe do encode things like tags in the nova instance object\nhttps://github.com/openstack/nova/blob/master/nova/objects/instance.py#L225\n\nso collection of sob obejct is not uncommon in genral.\n\ni.e. the pic_requests\n\nhttps://github.com/openstack/nova/blob/master/nova/objects/instance.py#L221","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1fe788d3b6d87e450b6050f4ca0a6e68a0424606","unresolved":false,"context_lines":[{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Proxified field so we can keep the previous value after an update"}],"source_content_type":"text/x-python","patch_set":3,"id":"56c020d8_f63c5af1","line":72,"in_reply_to":"f0f93980_589420c9","updated":"2026-09-03 10:50:39.000000000","message":"Acknowledged","commit_id":"113364e76b6f0544d134059ab971c75f762fba19"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"d6a1eeaa50892fd7c1e282dfd8e634eb8a4e5de3","unresolved":false,"context_lines":[{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"}],"source_content_type":"text/x-python","patch_set":5,"id":"8e69450a_c196f7ec","line":70,"updated":"2026-08-05 18:57:54.000000000","message":"The AuditPipeline OVO field defines auto_trigger with default\u003dTrue, but the SQLAlchemy model column defaults to False and the DB API create_audit_pipeline method forces False when the value is not provided.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Creating a pipeline via the OVO layer without explicitly setting auto_trigger defaults to True, while creating via DB API directly defaults to False. This inconsistency can cause unexpected behavior depending on the creation path and makes the API contract ambiguous.\n\n**Suggestion**:\nAlign the default value across all layers. The OVO field default should match the model column default and the DB API logic. If the intended default is False, set the OVO field to default\u003dFalse; if True, update the model and DB API accordingly.","commit_id":"9130d69f23dcbbb8fbc9aac60db53f62114dac47"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"cee6e6c3d2e17fd83268e6bc5d2e76856d01e228","unresolved":false,"context_lines":[{"line_number":216,"context_line":"        \u0027goal\u0027: (objects.Goal, \u0027goal_id\u0027),"},{"line_number":217,"context_line":"        \u0027strategy\u0027: (objects.Strategy, \u0027strategy_id\u0027),"},{"line_number":218,"context_line":"    }"},{"line_number":219,"context_line":""},{"line_number":220,"context_line":"    @classmethod"},{"line_number":221,"context_line":"    @base.remotable"},{"line_number":222,"context_line":"    def get(cls, context, stage_id, eager\u003dFalse):"}],"source_content_type":"text/x-python","patch_set":6,"id":"9b726261_369f99fc","line":219,"updated":"2026-08-07 19:53:37.000000000","message":"AuditPipelineStage.get() accepts eager\u003dFalse but does not forward it to either get_by_id() or get_by_uuid(). Additionally, get_by_uuid() accepts eager but does not pass it to the DB API call get_audit_pipeline_stage_by_uuid(), and get_by_id() does not accept eager at all. Eager loading for stage...\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Callers requesting eager loading of related objects (audit_pipeline, goal, strategy) for a stage will silently get lazy-loaded relationships, potentially causing N+1 query problems or unexpected behavior when accessing related object fields.\n\n**Suggestion**:\nForward the eager parameter through get -\u003e get_by_id/get_by_uuid -\u003e dbapi call, matching the AuditPipeline pattern. Add eager parameter to get_by_id and to the DB API get_audit_pipeline_stage_by_uuid method, and pass eager in both branches of get().","commit_id":"10a2c0cc44f7bb619e4ecd8adb25789369d09f5c"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"3231bcd0351d5dd9b559ce54be11c021f4495731","unresolved":false,"context_lines":[{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"    }"}],"source_content_type":"text/x-python","patch_set":7,"id":"f47dd267_bf0af64e","line":70,"updated":"2026-08-12 12:42:54.000000000","message":"The AuditPipeline OVO field declares auto_trigger with default\u003dTrue, but the SQLAlchemy model uses default\u003dFalse and create_audit_pipeline defaults it to False when not provided. This creates inconsistent behavior depending on whether the object is created via the OVO layer or the DB API directly.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Creating an AuditPipeline via the OVO without explicitly setting auto_trigger will default to True, while creating via the DBAPI without the key will default to False. This can lead to unexpected auto-triggering behavior depending on the code path used.\n\n**Recommendation**:\nAlign the defaults: either set the OVO field to `ovo_fields.BooleanField()` (no default, matching Audit) or `ovo_fields.BooleanField(default\u003dFalse)` to match the DBAPI and model behavior. The existing Audit object pattern uses no OVO default.","commit_id":"789c2a9522ce4a9cf453ec24251c64a9a7b7736c"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"19d35d1b36388f2b0e8dc2c4679d4cff20e92c38","unresolved":false,"context_lines":[{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    @classmethod"},{"line_number":130,"context_line":"    @base.remotable"},{"line_number":131,"context_line":"    def get_by_name(cls, context, name, eager\u003dFalse):"},{"line_number":132,"context_line":"        db_obj \u003d cls.dbapi.get_audit_pipeline_by_name("},{"line_number":133,"context_line":"            context, name, eager\u003deager"},{"line_number":134,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":9,"id":"47b1c656_00578ced","line":131,"updated":"2026-08-13 19:50:28.000000000","message":"The AuditPipeline object\u0027s get_by_name method calls cls.dbapi.get_audit_pipeline_by_name(), and the SQLAlchemy Connection implements it, but no corresponding abstract method is declared in BaseConnection (db/api.py). This is an incomplete API contract that will cause confusion for any alternative...\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Any alternative DB backend implementing BaseConnection would have no contract for get_audit_pipeline_by_name and would raise AttributeError at runtime if the object method is called.\n\n**Suggestion**:\nAdd @abc.abstractmethod get_audit_pipeline_by_name(self, context, audit_pipeline_name, eager\u003dFalse) to BaseConnection in watcher/db/api.py, matching the pattern used by get_audit_template_by_name.","commit_id":"496048120d81c235b93ba7a6ce067bf0e75c6aca"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"19d35d1b36388f2b0e8dc2c4679d4cff20e92c38","unresolved":false,"context_lines":[{"line_number":238,"context_line":""},{"line_number":239,"context_line":"    @classmethod"},{"line_number":240,"context_line":"    @base.remotable"},{"line_number":241,"context_line":"    def get_by_id(cls, context, stage_id):"},{"line_number":242,"context_line":"        db_obj \u003d cls.dbapi.get_audit_pipeline_stage_by_id(context, stage_id)"},{"line_number":243,"context_line":"        return cls._from_db_object(cls(context), db_obj)"},{"line_number":244,"context_line":""}],"source_content_type":"text/x-python","patch_set":9,"id":"3f780138_e3d4027a","line":241,"updated":"2026-08-13 19:50:28.000000000","message":"The AuditPipelineStage object\u0027s get_by_id method does not accept or pass an eager parameter, while get_by_uuid does. The get classmethod also ignores eager for the get_by_id path. This creates an inconsistency where eager loading works via uuid but not via id.\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Callers requesting eager loading of AuditPipelineStage via integer id will not get eager-loaded relationships, which could cause N+1 query issues or missing relationship data.\n\n**Recommendation**:\nAdd eager\u003dFalse parameter to AuditPipelineStage.get_by_id and pass it through to cls.dbapi.get_audit_pipeline_stage_by_id(context, stage_id, eager\u003deager) and update the get() method to pass eager to get_by_id.","commit_id":"496048120d81c235b93ba7a6ce067bf0e75c6aca"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"46bc16e50d9b02e033c33eb0cef2a9e0ee579b07","unresolved":false,"context_lines":[{"line_number":238,"context_line":""},{"line_number":239,"context_line":"    @classmethod"},{"line_number":240,"context_line":"    @base.remotable"},{"line_number":241,"context_line":"    def get_by_id(cls, context, stage_id):"},{"line_number":242,"context_line":"        db_obj \u003d cls.dbapi.get_audit_pipeline_stage_by_id(context, stage_id)"},{"line_number":243,"context_line":"        return cls._from_db_object(cls(context), db_obj)"},{"line_number":244,"context_line":""}],"source_content_type":"text/x-python","patch_set":12,"id":"d9963d10_cf30059e","line":241,"updated":"2026-08-19 20:36:29.000000000","message":"AuditPipelineStage.get() accepts eager but calls get_by_id without it; get_by_id has no eager parameter; get_by_uuid() accepts eager but calls dbapi.get_audit_pipeline_stage_by_uuid(context, uuid) without forwarding it; list() does not pass eager to the dbapi list call; and the abstract BaseConnection.get_audit_pipeline_stage_list omits the eager parameter even though get_audit_pipeline_stage_by_id/_by_uuid declare it. Every other watcher object (AuditPipeline, ActionPlan, Audit) threads eager end to end.\n\n**Severity**: SUGGESTION | **Confidence**: 0.85\n\n**Impact**: Callers passing eager\u003dTrue silently get non-joined loads: _from_db_object\u0027s fallback issues one related_obj_cls.get() per relationship (N+1 queries per stage), and the declared contract of the abstract DB API does not match the implemented surface, so parallel implementations can drift in behavior.\n\n**Recommendation**:\nThread eager through AuditPipelineStage.get/get_by_id/get_by_uuid/list into the dbapi calls and add the eager parameter to the abstract BaseConnection.get_audit_pipeline_stage_list signature and docstring to match the implementation.","commit_id":"bbcfe5be54863e5aa876ecbb5a9b8eb8db2e2a4f"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":74,"context_line":"            \u0027AuditPipelineStage\u0027, nullable\u003dTrue"},{"line_number":75,"context_line":"        ),"},{"line_number":76,"context_line":"    }"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    object_list_fields \u003d {"},{"line_number":79,"context_line":"        \u0027stages\u0027: (\u0027AuditPipelineStage\u0027, \u0027audit_pipeline_id\u0027)"},{"line_number":80,"context_line":"    }"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"}],"source_content_type":"text/x-python","patch_set":17,"id":"c1c56b41_540804d1","line":80,"range":{"start_line":77,"start_character":1,"end_line":80,"end_character":5},"updated":"2026-09-01 16:27:25.000000000","message":"this seems wrong to me you shoudl nto need this","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":false,"context_lines":[{"line_number":74,"context_line":"            \u0027AuditPipelineStage\u0027, nullable\u003dTrue"},{"line_number":75,"context_line":"        ),"},{"line_number":76,"context_line":"    }"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    object_list_fields \u003d {"},{"line_number":79,"context_line":"        \u0027stages\u0027: (\u0027AuditPipelineStage\u0027, \u0027audit_pipeline_id\u0027)"},{"line_number":80,"context_line":"    }"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"}],"source_content_type":"text/x-python","patch_set":17,"id":"d62c223a_d845a577","line":80,"range":{"start_line":77,"start_character":1,"end_line":80,"end_character":5},"in_reply_to":"1b27d765_c9d931ba","updated":"2026-09-02 11:45:54.000000000","message":"Acknowledged","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"4424fa87db9910c37d91e410f4660e5249bd500f","unresolved":true,"context_lines":[{"line_number":74,"context_line":"            \u0027AuditPipelineStage\u0027, nullable\u003dTrue"},{"line_number":75,"context_line":"        ),"},{"line_number":76,"context_line":"    }"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    object_list_fields \u003d {"},{"line_number":79,"context_line":"        \u0027stages\u0027: (\u0027AuditPipelineStage\u0027, \u0027audit_pipeline_id\u0027)"},{"line_number":80,"context_line":"    }"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"}],"source_content_type":"text/x-python","patch_set":17,"id":"1b27d765_c9d931ba","line":80,"range":{"start_line":77,"start_character":1,"end_line":80,"end_character":5},"in_reply_to":"5ada80f7_887b0edd","updated":"2026-09-01 21:59:01.000000000","message":"replacing this with a optional_fields + _from_db_object implementation in audit_pipeline.py","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":74,"context_line":"            \u0027AuditPipelineStage\u0027, nullable\u003dTrue"},{"line_number":75,"context_line":"        ),"},{"line_number":76,"context_line":"    }"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    object_list_fields \u003d {"},{"line_number":79,"context_line":"        \u0027stages\u0027: (\u0027AuditPipelineStage\u0027, \u0027audit_pipeline_id\u0027)"},{"line_number":80,"context_line":"    }"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"}],"source_content_type":"text/x-python","patch_set":17,"id":"5ada80f7_887b0edd","line":80,"range":{"start_line":77,"start_character":1,"end_line":80,"end_character":5},"in_reply_to":"c1c56b41_540804d1","updated":"2026-09-01 20:00:06.000000000","message":"this info is used in _from_db_object to list and load all stages info. If can remove if we implement the _from_db_object in AuditPipeline and AuditPipelineStage.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":true,"context_lines":[{"line_number":20,"context_line":"from watcher.db import api as db_api"},{"line_number":21,"context_line":"from watcher.objects import base"},{"line_number":22,"context_line":"from watcher.objects import fields as wfields"},{"line_number":23,"context_line":""},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"class State:"},{"line_number":26,"context_line":"    PENDING \u003d \u0027PENDING\u0027"},{"line_number":27,"context_line":"    ONGOING \u003d \u0027ONGOING\u0027"},{"line_number":28,"context_line":"    SUCCEEDED \u003d \u0027SUCCEEDED\u0027"},{"line_number":29,"context_line":"    FAILED \u003d \u0027FAILED\u0027"},{"line_number":30,"context_line":"    CANCELLED \u003d \u0027CANCELLED\u0027"},{"line_number":31,"context_line":"    DELETED \u003d \u0027DELETED\u0027"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"class AuditPipelineStateTransitionManager:"}],"source_content_type":"text/x-python","patch_set":18,"id":"822cb2ba_0c87528b","line":31,"range":{"start_line":23,"start_character":1,"end_line":31,"end_character":23},"updated":"2026-09-02 11:45:54.000000000","message":"if this was just normal python it should be an enum\n\n```\nimport enum\n\n\nclass State(enum.StrEnum):\n    PENDING \u003d \u0027PENDING\u0027\n    ONGOING \u003d \u0027ONGOING\u0027\n    SUCCEEDED \u003d \u0027SUCCEEDED\u0027\n    FAILED \u003d \u0027FAILED\u0027\n    CANCELLED \u003d \u0027CANCELLED\u0027\n    DELETED \u003d \u0027DELETED\u0027\n```\nbut since your using it in an ovo\n\n\n\n```suggestion\nfrom oslo_versionedobjects import fields\n\nclass State(fields.Enum):\n    PENDING \u003d \u0027PENDING\u0027\n    ONGOING \u003d \u0027ONGOING\u0027\n    SUCCEEDED \u003d \u0027SUCCEEDED\u0027\n    FAILED \u003d \u0027FAILED\u0027\n    CANCELLED \u003d \u0027CANCELLED\u0027\n    DELETED \u003d \u0027DELETED\u0027\n\n    ALL \u003d (\n        PENDING,\n        ONGOING,\n        SUCCEEDED,\n        FAILED,\n        CANCELLED,\n        DELETED,\n    )\n\n    def __init__(self):\n        super().__init__(valid_values\u003dState.ALL)\n        \nclass StateField(fields.BaseEnumField):\n    AUTO_TYPE \u003d State()\n```","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1f5986c9af76694577237d9cfa7482d88729ec7f","unresolved":false,"context_lines":[{"line_number":20,"context_line":"from watcher.db import api as db_api"},{"line_number":21,"context_line":"from watcher.objects import base"},{"line_number":22,"context_line":"from watcher.objects import fields as wfields"},{"line_number":23,"context_line":""},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"class State:"},{"line_number":26,"context_line":"    PENDING \u003d \u0027PENDING\u0027"},{"line_number":27,"context_line":"    ONGOING \u003d \u0027ONGOING\u0027"},{"line_number":28,"context_line":"    SUCCEEDED \u003d \u0027SUCCEEDED\u0027"},{"line_number":29,"context_line":"    FAILED \u003d \u0027FAILED\u0027"},{"line_number":30,"context_line":"    CANCELLED \u003d \u0027CANCELLED\u0027"},{"line_number":31,"context_line":"    DELETED \u003d \u0027DELETED\u0027"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"class AuditPipelineStateTransitionManager:"}],"source_content_type":"text/x-python","patch_set":18,"id":"2e100007_8ae3eeb0","line":31,"range":{"start_line":23,"start_character":1,"end_line":31,"end_character":23},"in_reply_to":"822cb2ba_0c87528b","updated":"2026-09-02 15:49:22.000000000","message":"ack, done! thanks!","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":true,"context_lines":[{"line_number":65,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":66,"context_line":"        \u0027name\u0027: ovo_fields.StringField(),"},{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":18,"id":"62ce139b_cbd6fcef","line":68,"range":{"start_line":68,"start_character":7,"end_line":68,"end_character":42},"updated":"2026-09-02 11:45:54.000000000","message":"then this should be \n\n```suggestion\n        \u0027state\u0027: StateField(nullable\u003dFalse, default\u003dState.PENDING),\n```","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1f5986c9af76694577237d9cfa7482d88729ec7f","unresolved":false,"context_lines":[{"line_number":65,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":66,"context_line":"        \u0027name\u0027: ovo_fields.StringField(),"},{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"}],"source_content_type":"text/x-python","patch_set":18,"id":"b421007a_dc7fad94","line":68,"range":{"start_line":68,"start_character":7,"end_line":68,"end_character":42},"in_reply_to":"62ce139b_cbd6fcef","updated":"2026-09-02 15:49:22.000000000","message":"Done","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"ed261d0b456c94ff67087c27171f9963efca48d3","unresolved":false,"context_lines":[{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField("}],"source_content_type":"text/x-python","patch_set":18,"id":"049b553f_32f6e30d","line":70,"updated":"2026-09-01 22:13:19.000000000","message":"The AuditPipeline object declares BooleanField(default\u003dTrue) for auto_trigger while the SQLAlchemy model default and create_audit_pipeline both force False when unset, so the same default decision has two owners with opposite values depending on the entry point.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: A pipeline created via the object layer without an explicit auto_trigger silently defaults to True (automatic triggering of action plans), while one created directly through the DB API defaults to False; a safety-relevant behavior depends on which layer the caller used. Existing tests always pass auto_trigger explicitly, so nothing pins the intended default.\n\n**Suggestion**:\nFollow the Audit pattern: drop the object-field default (BooleanField()) so the DB layer owns the default, or align both layers on one documented value; add a test creating a pipeline via objects.AuditPipeline without setting auto_trigger and asserting the persisted default.","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"fee626e5fe703265b4730ec6f77d9358920e8b61","unresolved":true,"context_lines":[{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField("}],"source_content_type":"text/x-python","patch_set":18,"id":"8a07b944_0750aec9","line":70,"in_reply_to":"049b553f_32f6e30d","updated":"2026-09-02 11:05:47.000000000","message":"Going to switch this to default\u003dFalse, same as Audit. Can be changed in a follow up or in the next PS if required.","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1f5986c9af76694577237d9cfa7482d88729ec7f","unresolved":false,"context_lines":[{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField("}],"source_content_type":"text/x-python","patch_set":18,"id":"ba73c067_ffb67abe","line":70,"in_reply_to":"35a11021_f9b71fdc","updated":"2026-09-02 15:49:22.000000000","message":"Done","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":true,"context_lines":[{"line_number":67,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":68,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":69,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":70,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(default\u003dTrue),"},{"line_number":71,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":72,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":73,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField("}],"source_content_type":"text/x-python","patch_set":18,"id":"35a11021_f9b71fdc","line":70,"in_reply_to":"8a07b944_0750aec9","updated":"2026-09-02 11:45:54.000000000","message":"ya i think that is for the best\nits a safer default and i think the right one at the api level as well but certenly at the object level","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"fee626e5fe703265b4730ec6f77d9358920e8b61","unresolved":true,"context_lines":[{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"}],"source_content_type":"text/x-python","patch_set":18,"id":"0ee10fe7_cce9a625","line":80,"range":{"start_line":80,"start_character":4,"end_line":80,"end_character":19},"updated":"2026-09-02 11:05:47.000000000","message":"it could be \"obj_extra_fields\" in the end too...","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1f5986c9af76694577237d9cfa7482d88729ec7f","unresolved":false,"context_lines":[{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"}],"source_content_type":"text/x-python","patch_set":18,"id":"d35b50df_9ada8237","line":80,"range":{"start_line":80,"start_character":4,"end_line":80,"end_character":19},"in_reply_to":"046d89ce_7cef1499","updated":"2026-09-02 15:49:22.000000000","message":"ack, done","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":true,"context_lines":[{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"}],"source_content_type":"text/x-python","patch_set":18,"id":"046d89ce_7cef1499","line":80,"range":{"start_line":80,"start_character":4,"end_line":80,"end_character":19},"in_reply_to":"0ee10fe7_cce9a625","updated":"2026-09-02 11:45:54.000000000","message":"it woudl be preferabl if you made this uppercase `OPTIONAL_FIELDS` to singnal that it shoudl be imutable","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":true,"context_lines":[{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"},{"line_number":84,"context_line":"    _old_state \u003d None"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    @property"},{"line_number":87,"context_line":"    def _obj_state(self):"},{"line_number":88,"context_line":"        return self._state"}],"source_content_type":"text/x-python","patch_set":18,"id":"2cb44ad5_77266451","line":85,"range":{"start_line":81,"start_character":1,"end_line":85,"end_character":1},"updated":"2026-09-02 11:45:54.000000000","message":"these are class varibels not instnace viabels so all auit pipeliens woudl share them i dont think that is what you want\n\nalso ovo have change state tracking built in so i dont think you shoudl implemtn that yourself","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1f5986c9af76694577237d9cfa7482d88729ec7f","unresolved":true,"context_lines":[{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"},{"line_number":84,"context_line":"    _old_state \u003d None"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    @property"},{"line_number":87,"context_line":"    def _obj_state(self):"},{"line_number":88,"context_line":"        return self._state"}],"source_content_type":"text/x-python","patch_set":18,"id":"d6268c79_c2fcec00","line":85,"range":{"start_line":81,"start_character":1,"end_line":85,"end_character":1},"in_reply_to":"2cb44ad5_77266451","updated":"2026-09-02 15:49:22.000000000","message":"This is following the same pattern from Audit, and _old_state helps to track state changes so we can use both Old an New state in notifications. Below we have the setters that will bind the value to instance not the class.\n\nAbout OVO changes tracking, it seems that it only tracks which fields have changed but do not store its previous value.\nNot sure how other projects handle this but we could consider investigating a better approach for it and apply to other classes like Audit and Action Plan","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1fe788d3b6d87e450b6050f4ca0a6e68a0424606","unresolved":false,"context_lines":[{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"},{"line_number":84,"context_line":"    _old_state \u003d None"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    @property"},{"line_number":87,"context_line":"    def _obj_state(self):"},{"line_number":88,"context_line":"        return self._state"}],"source_content_type":"text/x-python","patch_set":18,"id":"4bbc6ac4_2abcb097","line":85,"range":{"start_line":81,"start_character":1,"end_line":85,"end_character":1},"in_reply_to":"42204130_0a39d81d","updated":"2026-09-03 10:50:39.000000000","message":"lets resolve this for now and come back to it after this feature is complete","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ab5a1d6a0bed525508bb2f7006ae498af9af5151","unresolved":true,"context_lines":[{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"},{"line_number":84,"context_line":"    _old_state \u003d None"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    @property"},{"line_number":87,"context_line":"    def _obj_state(self):"},{"line_number":88,"context_line":"        return self._state"}],"source_content_type":"text/x-python","patch_set":18,"id":"42204130_0a39d81d","line":85,"range":{"start_line":81,"start_character":1,"end_line":85,"end_character":1},"in_reply_to":"cd9ec64c_57baef11","updated":"2026-09-02 23:28:26.000000000","message":"yeah, it is possible that we could get rid of this together with obj_refresh, it will require a bit more investigation with both","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":true,"context_lines":[{"line_number":78,"context_line":"    # Fields that are not columns on the audit_pipelines table. They are"},{"line_number":79,"context_line":"    # loaded from related tables only when eager loading is requested."},{"line_number":80,"context_line":"    optional_fields \u003d [\u0027stages\u0027]"},{"line_number":81,"context_line":""},{"line_number":82,"context_line":"    # Proxified field so we can keep the previous value after an update"},{"line_number":83,"context_line":"    _state \u003d None"},{"line_number":84,"context_line":"    _old_state \u003d None"},{"line_number":85,"context_line":""},{"line_number":86,"context_line":"    @property"},{"line_number":87,"context_line":"    def _obj_state(self):"},{"line_number":88,"context_line":"        return self._state"}],"source_content_type":"text/x-python","patch_set":18,"id":"cd9ec64c_57baef11","line":85,"range":{"start_line":81,"start_character":1,"end_line":85,"end_character":1},"in_reply_to":"d6268c79_c2fcec00","updated":"2026-09-02 19:41:05.000000000","message":"in most project we dont keep obejct aroun in memroy we pull the latest version form the db when we neede it. the probel with watcehr is it not using rpcs calls to compunicate state thanges it doing a direct db  update and we had to poll in places\n\n\nso you shoud not need to trac state cahgne in the object in general outside fo what ovo does since you do a read and modify it teh read obejct with the updated fiels and save that effetivly.\n\nin realciatyi think we are doing a select for update or other optimized save query but i dont recall off the top of my head what the sqlachme expression end up being.","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"ed261d0b456c94ff67087c27171f9963efca48d3","unresolved":false,"context_lines":[{"line_number":165,"context_line":""},{"line_number":166,"context_line":"    @classmethod"},{"line_number":167,"context_line":"    @base.remotable"},{"line_number":168,"context_line":"    def get_by_name(cls, context, name, eager\u003dFalse):"},{"line_number":169,"context_line":"        db_obj \u003d cls.dbapi.get_audit_pipeline_by_name("},{"line_number":170,"context_line":"            context, name, eager\u003deager"},{"line_number":171,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":18,"id":"a2ce5989_83994c13","line":168,"updated":"2026-09-01 22:13:19.000000000","message":"AuditPipeline.get_by_name calls cls.dbapi.get_audit_pipeline_by_name, implemented on Connection but not declared as an abstract method in watcher/db/api.py BaseConnection - the only pipeline method left out, and inconsistent with get_audit_template_by_name for the analogous resource.\n\n**Severity**: SUGGESTION | **Confidence**: 0.85\n\n**Impact**: The DB API contract does not guarantee a method the object layer depends on: an alternative backend implementing BaseConnection would appear complete yet fail at runtime only when AuditPipeline.get_by_name is invoked.\n\n**Recommendation**:\nAdd the @abc.abstractmethod get_audit_pipeline_by_name (with the usual docstring) to BaseConnection next to get_audit_pipeline_by_uuid.","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"1f5986c9af76694577237d9cfa7482d88729ec7f","unresolved":false,"context_lines":[{"line_number":165,"context_line":""},{"line_number":166,"context_line":"    @classmethod"},{"line_number":167,"context_line":"    @base.remotable"},{"line_number":168,"context_line":"    def get_by_name(cls, context, name, eager\u003dFalse):"},{"line_number":169,"context_line":"        db_obj \u003d cls.dbapi.get_audit_pipeline_by_name("},{"line_number":170,"context_line":"            context, name, eager\u003deager"},{"line_number":171,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":18,"id":"813802b8_daca92f1","line":168,"in_reply_to":"9962c3eb_30001c23","updated":"2026-09-02 15:49:22.000000000","message":"Done","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"fee626e5fe703265b4730ec6f77d9358920e8b61","unresolved":true,"context_lines":[{"line_number":165,"context_line":""},{"line_number":166,"context_line":"    @classmethod"},{"line_number":167,"context_line":"    @base.remotable"},{"line_number":168,"context_line":"    def get_by_name(cls, context, name, eager\u003dFalse):"},{"line_number":169,"context_line":"        db_obj \u003d cls.dbapi.get_audit_pipeline_by_name("},{"line_number":170,"context_line":"            context, name, eager\u003deager"},{"line_number":171,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":18,"id":"9962c3eb_30001c23","line":168,"in_reply_to":"a2ce5989_83994c13","updated":"2026-09-02 11:05:47.000000000","message":"we plan to remove this interface so I may or not include in the next PS or follow up","commit_id":"01455950b5a0be6f4b77b38bf4b21483ec334f60"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":true,"context_lines":[{"line_number":220,"context_line":"    def refresh(self, eager\u003dFalse):"},{"line_number":221,"context_line":"        current \u003d self.get_by_uuid(self._context, uuid\u003dself.uuid, eager\u003deager)"},{"line_number":222,"context_line":"        self.obj_refresh(current)"},{"line_number":223,"context_line":""},{"line_number":224,"context_line":"    @base.remotable"},{"line_number":225,"context_line":"    def soft_delete(self):"},{"line_number":226,"context_line":"        self.state \u003d State.DELETED"},{"line_number":227,"context_line":"        self.save()"},{"line_number":228,"context_line":"        self.dbapi.soft_delete_audit_pipeline(self.uuid)"},{"line_number":229,"context_line":""},{"line_number":230,"context_line":"    @base.remotable"},{"line_number":231,"context_line":"    def destroy(self):"},{"line_number":232,"context_line":"        self.dbapi.destroy_audit_pipeline(self.uuid)"},{"line_number":233,"context_line":"        self.obj_reset_changes()"},{"line_number":234,"context_line":""},{"line_number":235,"context_line":""},{"line_number":236,"context_line":"@base.WatcherObjectRegistry.register"},{"line_number":237,"context_line":"class AuditPipelineStage("}],"source_content_type":"text/x-python","patch_set":19,"id":"27af5e82_5ab273b3","line":234,"range":{"start_line":223,"start_character":1,"end_line":234,"end_character":1},"updated":"2026-09-02 19:41:05.000000000","message":"so the pipeline stage deleting shoudl eb doen here if we follow the same pattehr as the effeicy indicatory for the action plan\n\nhttps://github.com/openstack/watcher/blob/master/watcher/objects/action_plan.py#L280-L361","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ab5a1d6a0bed525508bb2f7006ae498af9af5151","unresolved":true,"context_lines":[{"line_number":220,"context_line":"    def refresh(self, eager\u003dFalse):"},{"line_number":221,"context_line":"        current \u003d self.get_by_uuid(self._context, uuid\u003dself.uuid, eager\u003deager)"},{"line_number":222,"context_line":"        self.obj_refresh(current)"},{"line_number":223,"context_line":""},{"line_number":224,"context_line":"    @base.remotable"},{"line_number":225,"context_line":"    def soft_delete(self):"},{"line_number":226,"context_line":"        self.state \u003d State.DELETED"},{"line_number":227,"context_line":"        self.save()"},{"line_number":228,"context_line":"        self.dbapi.soft_delete_audit_pipeline(self.uuid)"},{"line_number":229,"context_line":""},{"line_number":230,"context_line":"    @base.remotable"},{"line_number":231,"context_line":"    def destroy(self):"},{"line_number":232,"context_line":"        self.dbapi.destroy_audit_pipeline(self.uuid)"},{"line_number":233,"context_line":"        self.obj_reset_changes()"},{"line_number":234,"context_line":""},{"line_number":235,"context_line":""},{"line_number":236,"context_line":"@base.WatcherObjectRegistry.register"},{"line_number":237,"context_line":"class AuditPipelineStage("}],"source_content_type":"text/x-python","patch_set":19,"id":"d42f9ee8_8ce57ef6","line":234,"range":{"start_line":223,"start_character":1,"end_line":234,"end_character":1},"in_reply_to":"27af5e82_5ab273b3","updated":"2026-09-02 23:28:26.000000000","message":"ack, done in next PS","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1fe788d3b6d87e450b6050f4ca0a6e68a0424606","unresolved":false,"context_lines":[{"line_number":220,"context_line":"    def refresh(self, eager\u003dFalse):"},{"line_number":221,"context_line":"        current \u003d self.get_by_uuid(self._context, uuid\u003dself.uuid, eager\u003deager)"},{"line_number":222,"context_line":"        self.obj_refresh(current)"},{"line_number":223,"context_line":""},{"line_number":224,"context_line":"    @base.remotable"},{"line_number":225,"context_line":"    def soft_delete(self):"},{"line_number":226,"context_line":"        self.state \u003d State.DELETED"},{"line_number":227,"context_line":"        self.save()"},{"line_number":228,"context_line":"        self.dbapi.soft_delete_audit_pipeline(self.uuid)"},{"line_number":229,"context_line":""},{"line_number":230,"context_line":"    @base.remotable"},{"line_number":231,"context_line":"    def destroy(self):"},{"line_number":232,"context_line":"        self.dbapi.destroy_audit_pipeline(self.uuid)"},{"line_number":233,"context_line":"        self.obj_reset_changes()"},{"line_number":234,"context_line":""},{"line_number":235,"context_line":""},{"line_number":236,"context_line":"@base.WatcherObjectRegistry.register"},{"line_number":237,"context_line":"class AuditPipelineStage("}],"source_content_type":"text/x-python","patch_set":19,"id":"b58f07cf_9aa7e8c1","line":234,"range":{"start_line":223,"start_character":1,"end_line":234,"end_character":1},"in_reply_to":"d42f9ee8_8ce57ef6","updated":"2026-09-03 10:50:39.000000000","message":"Acknowledged","commit_id":"25601f9f554217c72cc41842e1ed947f60b9ac0a"}],"watcher/objects/base.py":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":101,"context_line":"    pass"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class WatcherPersistentObject:"},{"line_number":105,"context_line":"    \"\"\"Mixin class for Persistent objects."},{"line_number":106,"context_line":""},{"line_number":107,"context_line":"    This adds the fields that we use in common for all persistent objects."}],"source_content_type":"text/x-python","patch_set":17,"id":"667b60db_63c62e8a","line":104,"range":{"start_line":104,"start_character":6,"end_line":104,"end_character":29},"updated":"2026-09-01 16:27:25.000000000","message":"this is doing a lot mroe then it shoudl have too\n\nhttps://github.com/openstack/nova/blob/d3a1c95d06c2d97368207c2728fc262796683ad8/nova/objects/base.py#L181-L191\n\nit shoudl not be providing any atibute son the obejct outside of fields and shoudl not have any addtional functions in general.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":101,"context_line":"    pass"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class WatcherPersistentObject:"},{"line_number":105,"context_line":"    \"\"\"Mixin class for Persistent objects."},{"line_number":106,"context_line":""},{"line_number":107,"context_line":"    This adds the fields that we use in common for all persistent objects."}],"source_content_type":"text/x-python","patch_set":17,"id":"06b7e05f_126663ec","line":104,"range":{"start_line":104,"start_character":6,"end_line":104,"end_character":29},"in_reply_to":"5b4ceee9_83496cec","updated":"2026-09-02 19:41:05.000000000","message":"Acknowledged","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":101,"context_line":"    pass"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class WatcherPersistentObject:"},{"line_number":105,"context_line":"    \"\"\"Mixin class for Persistent objects."},{"line_number":106,"context_line":""},{"line_number":107,"context_line":"    This adds the fields that we use in common for all persistent objects."}],"source_content_type":"text/x-python","patch_set":17,"id":"31451f1c_961295a1","line":104,"range":{"start_line":104,"start_character":6,"end_line":104,"end_character":29},"in_reply_to":"667b60db_63c62e8a","updated":"2026-09-01 20:00:06.000000000","message":"Ack, I don\u0027t plan to remove it but we can stop extending it.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":101,"context_line":"    pass"},{"line_number":102,"context_line":""},{"line_number":103,"context_line":""},{"line_number":104,"context_line":"class WatcherPersistentObject:"},{"line_number":105,"context_line":"    \"\"\"Mixin class for Persistent objects."},{"line_number":106,"context_line":""},{"line_number":107,"context_line":"    This adds the fields that we use in common for all persistent objects."}],"source_content_type":"text/x-python","patch_set":17,"id":"5b4ceee9_83496cec","line":104,"range":{"start_line":104,"start_character":6,"end_line":104,"end_character":29},"in_reply_to":"667b60db_63c62e8a","updated":"2026-09-01 20:00:06.000000000","message":"ok, we might need to work on all objects in a new change, tracking as tech debt","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":112,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":113,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":114,"context_line":"    }"},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"    # Mapping between the object field name and a 2-tuple pair composed of"},{"line_number":117,"context_line":"    # its object type (e.g. objects.RelatedObject) and the name of the"},{"line_number":118,"context_line":"    # model field related ID (or UUID) foreign key field."},{"line_number":119,"context_line":"    # e.g.:"},{"line_number":120,"context_line":"    #"},{"line_number":121,"context_line":"    # fields \u003d {"},{"line_number":122,"context_line":"    #     # [...]"},{"line_number":123,"context_line":"    #     \u0027related_object_id\u0027: ovo_fields.IntegerField(),  # Foreign key"},{"line_number":124,"context_line":"    #     \u0027related_object\u0027: wfields.ObjectField(\u0027RelatedObject\u0027),"},{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"}],"source_content_type":"text/x-python","patch_set":17,"id":"b7ba2e08_2ffddb22","line":127,"range":{"start_line":115,"start_character":1,"end_line":127,"end_character":22},"updated":"2026-09-01 16:27:25.000000000","message":"oh we still have the old mapping code in our api\n\nthis was deprecated years ago we shoudl not be usign this anywhere any mroe or adding a collection equivletn\n\nthis is ment to be a property now provdied by the base ovo impelaiton\n\nhttps://github.com/openstack/oslo.versionedobjects/blob/6fc2dcc0dfc3df98e94131dd7d06320cebb97f3d/oslo_versionedobjects/base.py#L928-L930\n\nwe shoudl delet ethis entrily.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":112,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":113,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":114,"context_line":"    }"},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"    # Mapping between the object field name and a 2-tuple pair composed of"},{"line_number":117,"context_line":"    # its object type (e.g. objects.RelatedObject) and the name of the"},{"line_number":118,"context_line":"    # model field related ID (or UUID) foreign key field."},{"line_number":119,"context_line":"    # e.g.:"},{"line_number":120,"context_line":"    #"},{"line_number":121,"context_line":"    # fields \u003d {"},{"line_number":122,"context_line":"    #     # [...]"},{"line_number":123,"context_line":"    #     \u0027related_object_id\u0027: ovo_fields.IntegerField(),  # Foreign key"},{"line_number":124,"context_line":"    #     \u0027related_object\u0027: wfields.ObjectField(\u0027RelatedObject\u0027),"},{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"}],"source_content_type":"text/x-python","patch_set":17,"id":"c03d3d87_fa6e1759","line":127,"range":{"start_line":115,"start_character":1,"end_line":127,"end_character":22},"in_reply_to":"8848e7a1_363bbf0f","updated":"2026-09-02 19:41:05.000000000","message":"Acknowledged","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":112,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":113,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":114,"context_line":"    }"},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"    # Mapping between the object field name and a 2-tuple pair composed of"},{"line_number":117,"context_line":"    # its object type (e.g. objects.RelatedObject) and the name of the"},{"line_number":118,"context_line":"    # model field related ID (or UUID) foreign key field."},{"line_number":119,"context_line":"    # e.g.:"},{"line_number":120,"context_line":"    #"},{"line_number":121,"context_line":"    # fields \u003d {"},{"line_number":122,"context_line":"    #     # [...]"},{"line_number":123,"context_line":"    #     \u0027related_object_id\u0027: ovo_fields.IntegerField(),  # Foreign key"},{"line_number":124,"context_line":"    #     \u0027related_object\u0027: wfields.ObjectField(\u0027RelatedObject\u0027),"},{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"}],"source_content_type":"text/x-python","patch_set":17,"id":"b7ee0006_90c72ffa","line":127,"range":{"start_line":115,"start_character":1,"end_line":127,"end_character":22},"in_reply_to":"b7ba2e08_2ffddb22","updated":"2026-09-01 20:00:06.000000000","message":"not sure iiuc but the property that you linked only return fields (and obj_extra_fields) keys. This objects_fields are here to map field name and its object type. I am not ssure how ovo handles this mapping today.\nWe could get rid of this only if we implement _from_db_object in all objects, since they all use the base static _from_db_object","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"4424fa87db9910c37d91e410f4660e5249bd500f","unresolved":true,"context_lines":[{"line_number":112,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":113,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":114,"context_line":"    }"},{"line_number":115,"context_line":""},{"line_number":116,"context_line":"    # Mapping between the object field name and a 2-tuple pair composed of"},{"line_number":117,"context_line":"    # its object type (e.g. objects.RelatedObject) and the name of the"},{"line_number":118,"context_line":"    # model field related ID (or UUID) foreign key field."},{"line_number":119,"context_line":"    # e.g.:"},{"line_number":120,"context_line":"    #"},{"line_number":121,"context_line":"    # fields \u003d {"},{"line_number":122,"context_line":"    #     # [...]"},{"line_number":123,"context_line":"    #     \u0027related_object_id\u0027: ovo_fields.IntegerField(),  # Foreign key"},{"line_number":124,"context_line":"    #     \u0027related_object\u0027: wfields.ObjectField(\u0027RelatedObject\u0027),"},{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"}],"source_content_type":"text/x-python","patch_set":17,"id":"8848e7a1_363bbf0f","line":127,"range":{"start_line":115,"start_character":1,"end_line":127,"end_character":22},"in_reply_to":"b7ee0006_90c72ffa","updated":"2026-09-01 21:59:01.000000000","message":"replacing this with a optional_fields + _from_db_object implementation in audit_pipeline.py","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"},{"line_number":131,"context_line":"    # (resolved lazily against the ``objects`` registry to allow forward"},{"line_number":132,"context_line":"    # references) and the name of the child model foreign key field pointing"},{"line_number":133,"context_line":"    # back to this object\u0027s ``id``."},{"line_number":134,"context_line":"    # e.g.:"},{"line_number":135,"context_line":"    #"},{"line_number":136,"context_line":"    # fields \u003d {"},{"line_number":137,"context_line":"    #     # [...]"},{"line_number":138,"context_line":"    #     \u0027children\u0027: ovo_fields.ListOfObjectsField(\u0027ChildObject\u0027),"},{"line_number":139,"context_line":"    # }"},{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."}],"source_content_type":"text/x-python","patch_set":17,"id":"e64a8ee8_ea8e81be","line":141,"range":{"start_line":128,"start_character":1,"end_line":141,"end_character":27},"updated":"2026-09-01 16:27:25.000000000","message":"this i thinkis an expclit anyti pattern\n\nit looks liek your tring to implemnt a manual form of \n\nhttps://github.com/openstack/oslo.versionedobjects/blob/6fc2dcc0dfc3df98e94131dd7d06320cebb97f3d/oslo_versionedobjects/base.py#L454-L479 but for list field \n\nwhile there is still legacy suprpot for child version in the list object\n\nhttps://github.com/openstack/oslo.versionedobjects/blob/6fc2dcc0dfc3df98e94131dd7d06320cebb97f3d/oslo_versionedobjects/base.py#L1078\n\nwe decied year ago to stop bumpint the version fo the container object when the version of the object in a collection changes.\n\n\nso addign a filed to an item in the liest bumpts the items version but not the compistat class that has the list.\n\nthere used to be the concoept of a obejct compatiablity mapp between standard obejct and all of ther subfield but we stop using those a decade ago\n\n\nteher helper fucntion to determin what subobejct and there version can be contiend in an obejct\n\nhttps://github.com/openstack/oslo.versionedobjects/blob/6fc2dcc0dfc3df98e94131dd7d06320cebb97f3d/oslo_versionedobjects/base.py#L1561-L1645\n\nbut we do not manually encode that in the jobs defintion any more.\n\ni think instead of implemting a custom easer loadign you shoudl be using\n\nhttps://github.com/openstack/oslo.versionedobjects/blob/6fc2dcc0dfc3df98e94131dd7d06320cebb97f3d/oslo_versionedobjects/base.py#L439-L452\n\nand if we want to lazy load a field like a collection fo obejct you shoudl  using the existing obj_extra_fields fucntionaly in ovo not invetning your own","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"4424fa87db9910c37d91e410f4660e5249bd500f","unresolved":true,"context_lines":[{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"},{"line_number":131,"context_line":"    # (resolved lazily against the ``objects`` registry to allow forward"},{"line_number":132,"context_line":"    # references) and the name of the child model foreign key field pointing"},{"line_number":133,"context_line":"    # back to this object\u0027s ``id``."},{"line_number":134,"context_line":"    # e.g.:"},{"line_number":135,"context_line":"    #"},{"line_number":136,"context_line":"    # fields \u003d {"},{"line_number":137,"context_line":"    #     # [...]"},{"line_number":138,"context_line":"    #     \u0027children\u0027: ovo_fields.ListOfObjectsField(\u0027ChildObject\u0027),"},{"line_number":139,"context_line":"    # }"},{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."}],"source_content_type":"text/x-python","patch_set":17,"id":"ec0a09a0_5fe3c43f","line":141,"range":{"start_line":128,"start_character":1,"end_line":141,"end_character":27},"in_reply_to":"03c149fe_06af66b0","updated":"2026-09-01 21:59:01.000000000","message":"replacing this with a optional_fields + _from_db_object implementation in audit_pipeline.py","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"},{"line_number":131,"context_line":"    # (resolved lazily against the ``objects`` registry to allow forward"},{"line_number":132,"context_line":"    # references) and the name of the child model foreign key field pointing"},{"line_number":133,"context_line":"    # back to this object\u0027s ``id``."},{"line_number":134,"context_line":"    # e.g.:"},{"line_number":135,"context_line":"    #"},{"line_number":136,"context_line":"    # fields \u003d {"},{"line_number":137,"context_line":"    #     # [...]"},{"line_number":138,"context_line":"    #     \u0027children\u0027: ovo_fields.ListOfObjectsField(\u0027ChildObject\u0027),"},{"line_number":139,"context_line":"    # }"},{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."}],"source_content_type":"text/x-python","patch_set":17,"id":"03c149fe_06af66b0","line":141,"range":{"start_line":128,"start_character":1,"end_line":141,"end_character":27},"in_reply_to":"e64a8ee8_ea8e81be","updated":"2026-09-01 20:00:06.000000000","message":"this is here to load the collection of objects, since they are not just a scalar copy loop. I think that we can avoid that by implementing _from_db_object in AuditPipeline.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":false,"context_lines":[{"line_number":125,"context_line":"    # }"},{"line_number":126,"context_line":"    # {\u0027related_object\u0027: (objects.RelatedObject, \u0027related_object_id\u0027)}"},{"line_number":127,"context_line":"    object_fields \u003d {}"},{"line_number":128,"context_line":""},{"line_number":129,"context_line":"    # Mapping between an object field name holding a collection of child"},{"line_number":130,"context_line":"    # objects and a 2-tuple pair composed of the child object class name"},{"line_number":131,"context_line":"    # (resolved lazily against the ``objects`` registry to allow forward"},{"line_number":132,"context_line":"    # references) and the name of the child model foreign key field pointing"},{"line_number":133,"context_line":"    # back to this object\u0027s ``id``."},{"line_number":134,"context_line":"    # e.g.:"},{"line_number":135,"context_line":"    #"},{"line_number":136,"context_line":"    # fields \u003d {"},{"line_number":137,"context_line":"    #     # [...]"},{"line_number":138,"context_line":"    #     \u0027children\u0027: ovo_fields.ListOfObjectsField(\u0027ChildObject\u0027),"},{"line_number":139,"context_line":"    # }"},{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."}],"source_content_type":"text/x-python","patch_set":17,"id":"8b07f13a_13c0fa09","line":141,"range":{"start_line":128,"start_character":1,"end_line":141,"end_character":27},"in_reply_to":"ec0a09a0_5fe3c43f","updated":"2026-09-02 11:45:54.000000000","message":"Acknowledged","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"        Checks for updated attributes in an object. Updates are applied from"}],"source_content_type":"text/x-python","patch_set":17,"id":"3591eca3_678eb943","line":143,"updated":"2026-09-01 16:27:25.000000000","message":"ovos has changed filed tracting by the way \n\nhttps://github.com/openstack/oslo.versionedobjects/blob/6fc2dcc0dfc3df98e94131dd7d06320cebb97f3d/oslo_versionedobjects/base.py#L481\n\nhttps://github.com/openstack/oslo.versionedobjects/blob/6fc2dcc0dfc3df98e94131dd7d06320cebb97f3d/oslo_versionedobjects/base.py#L848-L912\n\nim not sure how refehs is being used in watcher but the fact its not using \n\nobj_what_changed or obj_get_changes is screaming techdebt that we shoudl be activly trying to remove","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"8873d8e4117b681aaee7d6966566afddf92ff547","unresolved":true,"context_lines":[{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"        Checks for updated attributes in an object. Updates are applied from"}],"source_content_type":"text/x-python","patch_set":17,"id":"f450e38e_84da803e","line":143,"in_reply_to":"3064bae4_1c26c92e","updated":"2026-09-02 11:45:54.000000000","message":"we likely should look at removign that old state trackign form those entirly im not conviced it is required but we can do that seperatly\n\ni think we shoudl start a seperte ovo modernisatoin branch and see how much fo our custom logic we can delegate back to ovo","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"4424fa87db9910c37d91e410f4660e5249bd500f","unresolved":true,"context_lines":[{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"        Checks for updated attributes in an object. Updates are applied from"}],"source_content_type":"text/x-python","patch_set":17,"id":"3064bae4_1c26c92e","line":143,"in_reply_to":"3419ba94_bc478d84","updated":"2026-09-01 21:59:01.000000000","message":"still keeping obj_refresh but implementing it in audit_pipeline instead. Since the class maintains a _old_state property, we can\u0027t update the entire object, so obj_refresh will update only the fields that changed. We may get rid of this in all by changing how we track old_state info when sending notifications..","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"        Checks for updated attributes in an object. Updates are applied from"}],"source_content_type":"text/x-python","patch_set":17,"id":"3419ba94_bc478d84","line":143,"in_reply_to":"3591eca3_678eb943","updated":"2026-09-01 20:00:06.000000000","message":"we will need to revisit later, but obj_refresh updates self with recent loaded DB object. I think we can simplify using obj_what_changed() here, but it seems that we would need to refacor how _old_state works in Audit and ActionPlan to get rid of this refresh (since it is used during the update to avoid overriding old_state)","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1fe788d3b6d87e450b6050f4ca0a6e68a0424606","unresolved":false,"context_lines":[{"line_number":140,"context_line":"    # {\u0027children\u0027: (\u0027ChildObject\u0027, \u0027parent_id\u0027)}"},{"line_number":141,"context_line":"    object_list_fields \u003d {}"},{"line_number":142,"context_line":""},{"line_number":143,"context_line":"    def obj_refresh(self, loaded_object):"},{"line_number":144,"context_line":"        \"\"\"Applies updates for objects that inherit from base.WatcherObject."},{"line_number":145,"context_line":""},{"line_number":146,"context_line":"        Checks for updated attributes in an object. Updates are applied from"}],"source_content_type":"text/x-python","patch_set":17,"id":"77020df6_a9154bc2","line":143,"in_reply_to":"f450e38e_84da803e","updated":"2026-09-03 10:50:39.000000000","message":"Acknowledged","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"3b2e8ac7f55f97f4d116e3f0b6797973eafbbe6a","unresolved":true,"context_lines":[{"line_number":161,"context_line":"                self[field] \u003d loaded_object[field]"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    @staticmethod"},{"line_number":164,"context_line":"    def _from_db_object(obj, db_object, eager\u003dFalse):"},{"line_number":165,"context_line":"        \"\"\"Converts a database entity to a formal object."},{"line_number":166,"context_line":""},{"line_number":167,"context_line":"        :param obj: An object of the class."}],"source_content_type":"text/x-python","patch_set":17,"id":"04c3dc4e_6706831c","line":164,"updated":"2026-09-01 16:27:25.000000000","message":"these change don\u0027t look correct to me\n\n\nlist field shoudl not be special as i menteion above\n\ntypically _from_db_object is not shared between classes so its not impelemted on teh abse class\n\n\nthey could be shared via the base clasee but to do that really requries it to be writen as a class method instead of static\n\nhere is a smaller example\n\n\nhttps://github.com/openstack/nova/blob/d3a1c95d06c2d97368207c2728fc262796683ad8/nova/objects/virtual_interface.py#L35-L76\n\nto do it genericlly you whav eto have a stnadard OPTIONAL_FIELDS \u003d []\nfield in the base class then write teh meod interms of cls.OPTIONAL_FIELDS \n\n\nunless we need to overid it it shoudl just be \n\n\n```\n    @classmethod\n    def _from_db_object(cls, db_obg,eager_load\u003dNone):\n        if eager_load is None:\n          eager_load \u003d set()\n        obj\u003dcls()\n        for field in cls.fields:\n            if not db_obj[field] and field in cls.OPTIONAL_FIELDS:\n                continue\n            elif field in cls.OPTIONAL_FIELDS and field not in eager_load:\n                continue\n            else:\n                setattr(obj, field, db_vif[field])\n        obj.obj_reset_changes()\n        return obj\n```\n\nby default and if we need to do a migration or simialr\nthen we soudl do that as an overied in teh resltvent class.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"ef30084b0119d1f9ebdbcc23ed354d3586d96841","unresolved":true,"context_lines":[{"line_number":161,"context_line":"                self[field] \u003d loaded_object[field]"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    @staticmethod"},{"line_number":164,"context_line":"    def _from_db_object(obj, db_object, eager\u003dFalse):"},{"line_number":165,"context_line":"        \"\"\"Converts a database entity to a formal object."},{"line_number":166,"context_line":""},{"line_number":167,"context_line":"        :param obj: An object of the class."}],"source_content_type":"text/x-python","patch_set":17,"id":"f10b5e5e_c0333a0a","line":164,"in_reply_to":"04c3dc4e_6706831c","updated":"2026-09-01 20:00:06.000000000","message":"Ack, we can circle back this later. I can push a new PS which does not extend this method anymore.","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"671926740035ef60c393df66e74bfada2882e347","unresolved":false,"context_lines":[{"line_number":161,"context_line":"                self[field] \u003d loaded_object[field]"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    @staticmethod"},{"line_number":164,"context_line":"    def _from_db_object(obj, db_object, eager\u003dFalse):"},{"line_number":165,"context_line":"        \"\"\"Converts a database entity to a formal object."},{"line_number":166,"context_line":""},{"line_number":167,"context_line":"        :param obj: An object of the class."}],"source_content_type":"text/x-python","patch_set":17,"id":"c2cca936_74a61acd","line":164,"in_reply_to":"f10b5e5e_c0333a0a","updated":"2026-09-02 19:41:05.000000000","message":"Acknowledged","commit_id":"5816d3c6bf87b583a83a11347c77f7e411e5bb42"}],"watcher/tests/unit/db/test_audit_pipeline.py":[{"author":{"_account_id":28006,"name":"teim-ci","display_name":"teim-ci","email":"ci@seanmooney.info","username":"ci-sean-mooney","status":"this is a third-party ci account run by sean-k-mooney on irc\nhosted at zuul.teim.app"},"tag":"autogenerated:zuul:automatic-ci","change_message_id":"de2448b66549ad762f65fbb14b25ba91eb44314e","unresolved":false,"context_lines":[{"line_number":230,"context_line":"            filters\u003d{\u0027audit_pipeline_uuid\u0027: self.pipeline[\u0027uuid\u0027]},"},{"line_number":231,"context_line":"        )"},{"line_number":232,"context_line":"        self.assertEqual(1, len(stages))"},{"line_number":233,"context_line":"        self.assertEqual(self.stage[\u0027uuid\u0027], stages[0][\u0027uuid\u0027])"},{"line_number":234,"context_line":""},{"line_number":235,"context_line":"    def test_get_audit_pipeline_stage_list_filter_goal_uuid(self):"},{"line_number":236,"context_line":"        stages \u003d self.dbapi.get_audit_pipeline_stage_list("}],"source_content_type":"text/x-python","patch_set":4,"id":"5ae73f7a_08751243","line":233,"updated":"2026-08-05 15:26:45.000000000","message":"The DB test test_soft_delete_audit_pipeline_cascades_stages calls soft_delete_audit_pipeline with the integer ID (self.pipeline[\u0027id\u0027]), but the object layer always calls it with a UUID. This means the cascade soft-delete bug (CF-001) is not caught by existing tests.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: The cascade soft-delete path with UUID input is untested, allowing a data-integrity bug to pass CI.\n\n**Suggestion**:\nAdd a test variant that calls soft_delete_audit_pipeline with the pipeline UUID and verifies stages are cascaded. This test would immediately expose the CF-001 bug.","commit_id":"bfe67fcdf5893e215f0cf37741fe89ede81bfb44"}]}
