)]}'
{"/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":"d7aef9cfeaa1c4b298c59fa9c48fdafb2ca2372f","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     ca9414f9 (Audit Pipeline Object and DBAPI)"},{"line_number":2,"context_line":"Author:     Douglas Viroel \u003cviroel@gmail.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-08-05 09:52:07 -0300"},{"line_number":4,"context_line":"Commit:     Douglas Viroel \u003cviroel@gmail.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":7,"id":"e3498743_e239df40","line":1,"updated":"2026-08-18 14:13:26.000000000","message":"The commit message bullet states \u0027Hook notifications into AuditPipeline.create(), .save(), .soft_delete() and PipelineHandler.execute() (start/end/error)\u0027. However, no PipelineHandler class exists anywhere in the tree, and send_action_notification is not called from any code path; the execution start/end/error notifications are only exercised by unit tests. A later paragraph does say send_action_notification is \u0027not yet wired in this commit\u0027, so the message contradicts itself and its own implementation, leaving the permanent history inaccurate about what this patch does.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Future readers and release-note authors relying on the commit history will believe execution-event notifications are wired into the decision engine when they are not, which can hide that audit_pipeline.execution.* events will never be emitted by the current tree.\n\n**Suggestion**:\nAmend the message to remove the PipelineHandler.execute() claim (or state that the execution start/end/error notifications are added but not yet emitted from any handler), keeping the sentence that send_action_notification is for future use.","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"227dd7704490c661718b5bbebd66ff77167246ef","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     ca9414f9 (Audit Pipeline Object and DBAPI)"},{"line_number":2,"context_line":"Author:     Douglas Viroel \u003cviroel@gmail.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-08-05 09:52:07 -0300"},{"line_number":4,"context_line":"Commit:     Douglas Viroel \u003cviroel@gmail.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":7,"id":"56c0f0ee_2775dcb5","line":1,"in_reply_to":"e3498743_e239df40","updated":"2026-08-19 20:13:06.000000000","message":"it is being used in other patches in the chain","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"},{"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":"48b7dcd45baea3d981298aa725734f83f06d1c1f","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     bbcfe5be (Audit Pipeline Object and DBAPI)"},{"line_number":2,"context_line":"Author:     Douglas Viroel \u003cviroel@gmail.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-08-05 09:52:07 -0300"},{"line_number":4,"context_line":"Commit:     Douglas Viroel \u003cviroel@gmail.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":8,"id":"d1868e9b_db5ad83d","line":1,"updated":"2026-08-19 20:47:47.000000000","message":"The commit message bullet states notifications are hooked into \u0027AuditPipeline.create(), .save(), .soft_delete() and PipelineHandler.execute() (start/end/error)\u0027. However, no PipelineHandler class or module exists anywhere in the tree (grep over watcher/ finds no match), and notifications.audit_pipeline.send_action_notification has no production callers outside the new unit tests. The message itself contradicts this bullet later: \u0027The send_action_notification provided in this patch if for future use of Audit Pipeline feature, but not yet wired in this commit.\u0027 One of the two statements is wrong; the accurate one is the latter. A reviewer or future archaeologist reading the permanent history would wrongly conclude execution start/end/error events are emitted today.\n\n**Severity**: WARNING | **Confidence**: 0.95\n\n**Impact**: The permanent change history is technically inaccurate about emitted events: operators or consumers auditing versioned notifications (e.g., waiting for audit_pipeline.execution.start/end/error) would believe those events are produced by this release when they are not, since no code path calls send_action_notification yet.\n\n**Suggestion**:\nReword the bullet to remove \u0027and PipelineHandler.execute() (start/end/error)\u0027 (or explicitly state the execution notifications are introduced but not yet wired to any executor, matching the later sentence), and fix \u0027if for\u0027 to \u0027is for\u0027 before merge.","commit_id":"0089aeb15c2016b193a22c6dece7efcb8432491a"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"296ea051d89793ecfa7d318e1229d6116f2e52fb","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":16,"id":"11b5b9a8_d96ec723","updated":"2026-09-03 12:42:39.000000000","message":"lgtm, thanks for explaining about the schema","commit_id":"0324e0fbd22b349b76cd14faf9b09713e451ef46"}],"doc/notification_samples/action_plan-cancel-end.json":[{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ba198b73c8b0baf4f0c14531f824e151aa2c156","unresolved":false,"context_lines":[{"line_number":3,"context_line":"  \"payload\": {"},{"line_number":4,"context_line":"    \"watcher_object.namespace\": \"watcher\","},{"line_number":5,"context_line":"    \"watcher_object.name\": \"ActionPlanCancelPayload\","},{"line_number":6,"context_line":"    \"watcher_object.version\": \"1.3\","},{"line_number":7,"context_line":"    \"watcher_object.data\": {"},{"line_number":8,"context_line":"      \"created_at\": \"2016-10-18T09:52:05Z\","},{"line_number":9,"context_line":"      \"deleted_at\": null,"}],"source_content_type":"application/json","patch_set":13,"id":"5f2e93a1_b8a58ef2","line":6,"updated":"2026-09-01 17:02:40.000000000","message":"i went back and forth on this a bit.\n\nfor the api sampels we expect one sample per microversion or affected denpoint.\n\ntecnially the notificaton have a simialr contract the only difference is there is no way to reqeust that they are backlevel since its a push model.\n\nto be fair for the notificaotn sample test we do tend to be a lot less trict with that and only track the latest version \n\n\nhttps://github.com/openstack/nova/blob/d3a1c95d06c2d97368207c2728fc262796683ad8/doc/notification_samples/instance-create-error.json\n\nso given that this is fine and consitent with other services.","commit_id":"85daa4dac35093948644942b412f755cdf30f6ac"}],"doc/notification_samples/action_plan-create.json":[{"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":"a9e5315f08422a2099d8b709fb48f4fe880163a9","unresolved":false,"context_lines":[{"line_number":20,"context_line":"      },"},{"line_number":21,"context_line":"      \"created_at\": null,"},{"line_number":22,"context_line":"      \"audit_uuid\": \"10a47dd1-4874-4298-91cf-eff046dbdb8d\","},{"line_number":23,"context_line":"      \"audit_pipeline_uuid\": null,"},{"line_number":24,"context_line":"      \"audit\": {"},{"line_number":25,"context_line":"        \"watcher_object.version\": \"1.0\","},{"line_number":26,"context_line":"        \"watcher_object.data\": {"}],"source_content_type":"application/json","patch_set":14,"id":"de0671c2_328ab7d4","line":23,"updated":"2026-09-01 22:24:25.000000000","message":"ActionPlanPayload (version 1.3) now always carries the nested nullable ObjectField \u0027audit_pipeline\u0027, so the serialized watcher_object.data for audit-linked action plans includes the key \"audit_pipeline\": null alongside the new \"audit_pipeline_uuid\": null. The nine updated action_plan sample files only add \"audit_pipeline_uuid\": null; none contains the \"audit_pipeline\" key, so every sample no longer matches what watcher actually emits for these events.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Consumers and documentation generated from the samples see a payload shape that differs from the emitted one (missing key). Since these files are the de-facto format reference for versioned notifications, the inconsistency misleads downstream implementers and future sample-based verification.\n\n**Suggestion**:\nAdd \"audit_pipeline\": null next to \"audit_pipeline_uuid\": null in the watcher_object.data of all nine action_plan-*.json samples (or regenerate the samples from an actual emission), matching what the updated tests assert.","commit_id":"6f40745b58ffbc139e4d2dfddb194f6e04299e97"},{"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":"c22bc25ce011a010dfd4529faec11da845b5de76","unresolved":false,"context_lines":[{"line_number":20,"context_line":"      },"},{"line_number":21,"context_line":"      \"created_at\": null,"},{"line_number":22,"context_line":"      \"audit_uuid\": \"10a47dd1-4874-4298-91cf-eff046dbdb8d\","},{"line_number":23,"context_line":"      \"audit_pipeline_uuid\": null,"},{"line_number":24,"context_line":"      \"audit\": {"},{"line_number":25,"context_line":"        \"watcher_object.version\": \"1.0\","},{"line_number":26,"context_line":"        \"watcher_object.data\": {"}],"source_content_type":"application/json","patch_set":16,"id":"cf4ace78_70595076","line":23,"updated":"2026-09-02 23:51:21.000000000","message":"ActionPlanPayload was bumped to 1.3 with a new nullable audit_pipeline object field, but the nine action_plan-* sample files were only updated with the version string and audit_pipeline_uuid. The actual serialized payload contains an additional audit_pipeline key (null for audit-based plans), because OVO obj_to_primitive emits nullable object fields that are set to None.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: The doc samples are the published versioned-notification contract; consumers that key on the full payload structure will encounter an undocumented audit_pipeline field in every action_plan create/update/delete/execution/cancel notification.\n\n**Suggestion**:\nAdd \"audit_pipeline\": null (or a TerseAuditPipelinePayload object for a pipeline-linked sample) to all nine action_plan-*.json sample payloads so they match the emitted payloads.","commit_id":"0324e0fbd22b349b76cd14faf9b09713e451ef46"}],"doc/notification_samples/audit-pipeline-delete.json":[{"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":"9911dc75c4211942cd2e0e923838b7e20238db76","unresolved":false,"context_lines":[{"line_number":1,"context_line":"{"},{"line_number":2,"context_line":"  \"priority\": \"INFO\","},{"line_number":3,"context_line":"  \"payload\": {"},{"line_number":4,"context_line":"    \"watcher_object.data\": {"}],"source_content_type":"application/json","patch_set":6,"id":"945c09fe_b87a29b9","line":1,"updated":"2026-08-18 13:32:06.000000000","message":"The newly added sample files contain message_id strings with non-hexadecimal characters (e.g. \u00273f6c4fgh-5c5c-6d5c-be9g-3c4d5e6f7a8b\u0027, \u00272e5b3efg-...\u0027, \u00274a7d5ghi-...\u0027, \u00276c9f7ijk-...\u0027), which are not valid UUIDs and break the convention of every other sample in doc/notification_samples/ (all use proper UUIDs for message_id, since oslo versionednotification serializes message_id as a UUID). Additionally, audit-pipeline-delete.json shows a populated deleted_at and stage content that the actual send_delete wiring cannot currently produce (see the soft_delete refresh issue), and audit-pipeline-execution-error.json\u0027s fault module_name references watcher.decision_engine.audit.pipeline, a module that does not exist in the tree.\n\n**Severity**: SUGGESTION | **Confidence**: 0.85\n\n**Impact**: The samples double as the machine-checked notification contract (they are loaded via the notification_sample decorator and consumed as API documentation); invalid UUIDs are sloppy documentation, and payload content that the code cannot actually emit misleads consumers integrating against these events.\n\n**Recommendation**:\nReplace the message_id values with real UUIDs, align audit-pipeline-delete.json with the payload the code path actually emits (after fixing the refresh), and either correct or defer the execution-error sample\u0027s module_name until the emitting handler exists.","commit_id":"ca8a370c127333bcbc2fca7b479eb7b2104ced32"}],"doc/notification_samples/audit-pipeline-execution-start.json":[{"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":"d7aef9cfeaa1c4b298c59fa9c48fdafb2ca2372f","unresolved":false,"context_lines":[{"line_number":22,"context_line":"  \"publisher_id\": \"infra-optim:localhost\","},{"line_number":23,"context_line":"  \"timestamp\": \"2016-10-18T09:52:05.219414\","},{"line_number":24,"context_line":"  \"event_type\": \"audit_pipeline.execution.start\","},{"line_number":25,"context_line":"  \"message_id\": \"4a7d5ghi-6d6d-7e6d-cf0h-4d5e6f7a8b9c\""},{"line_number":26,"context_line":"}"}],"source_content_type":"application/json","patch_set":7,"id":"25d9e5de_65791ce8","line":25,"updated":"2026-08-18 14:13:26.000000000","message":"Five of the six new doc/notification_samples/audit-pipeline-*.json files use message_id strings containing non-hexadecimal characters (g, h, i, j), e.g. \u00274a7d5ghi-6d6d-7e6d-cf0h-4d5e6f7a8b9c\u0027. These samples are the published reference for the new versioned notifications (consumed by documentation generation), and oslo.messaging generates real UUIDs for message_id, so the examples show a value format that can never occur.\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Impact**: Published documentation examples show malformed identifiers for the new event types; minor consumer confusion or failed copy-paste validation, no runtime impact.\n\n**Recommendation**:\nReplace the five malformed message_id strings with valid UUIDs, e.g. generate with uuid.uuid4(), matching the create sample\u0027s format.","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"227dd7704490c661718b5bbebd66ff77167246ef","unresolved":false,"context_lines":[{"line_number":22,"context_line":"  \"publisher_id\": \"infra-optim:localhost\","},{"line_number":23,"context_line":"  \"timestamp\": \"2016-10-18T09:52:05.219414\","},{"line_number":24,"context_line":"  \"event_type\": \"audit_pipeline.execution.start\","},{"line_number":25,"context_line":"  \"message_id\": \"4a7d5ghi-6d6d-7e6d-cf0h-4d5e6f7a8b9c\""},{"line_number":26,"context_line":"}"}],"source_content_type":"application/json","patch_set":7,"id":"459faf00_5bd956cf","line":25,"in_reply_to":"25d9e5de_65791ce8","updated":"2026-08-19 20:13:06.000000000","message":"Fixes in next PS!","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"}],"watcher/notifications/action_plan.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":"942fe1f13ac66029bb42676db5af73ec1695eeed","unresolved":false,"context_lines":[{"line_number":51,"context_line":"    # Version 1.1: Changed \u0027global_efficacy\u0027 type Dictionary to List"},{"line_number":52,"context_line":"    # Version 1.2: Added \u0027status_message\u0027 field"},{"line_number":53,"context_line":"    # Version 1.3: audit_uuid is nullable; audit_pipeline_uuid added"},{"line_number":54,"context_line":"    VERSION \u003d \u00271.3\u0027"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    fields \u003d {"},{"line_number":57,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"}],"source_content_type":"text/x-python","patch_set":11,"id":"c86ae400_ba2812f7","line":54,"updated":"2026-08-27 21:23:13.000000000","message":"TerseActionPlanPayload was bumped to 1.3 (audit_uuid nullable, new audit_pipeline_uuid field), and every action.* notification embeds this payload via _get_action_plan_payload. The patch updated the nine action_plan-* sample files but left the nine doc/notification_samples/action-*.json files unchanged: they still declare the nested TerseActionPlanPayload as version 1.0 and omit the audit_pipeline_uuid key that now appears in emitted action.* payloads.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Consumers parsing action.create/update/execution/cancel/delete events from the published sample docs will not know audit_pipeline_uuid is now present and will see a documented payload version two releases behind what is emitted, breaking sample-driven consumers.\n\n**Suggestion**:\nUpdate the nine doc/notification_samples/action-*.json files: bump the nested TerseActionPlanPayload version to 1.3 and add \"audit_pipeline_uuid\": null alongside audit_uuid, mirroring the action_plan-* sample updates already in this patch.","commit_id":"d7357f1e541d1f5e56def7f426afe63010db4e5a"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ba198b73c8b0baf4f0c14531f824e151aa2c156","unresolved":true,"context_lines":[{"line_number":92,"context_line":""},{"line_number":93,"context_line":"    fields \u003d {"},{"line_number":94,"context_line":"        \u0027audit\u0027: ovo_fields.ObjectField(\u0027TerseAuditPayload\u0027, nullable\u003dTrue),"},{"line_number":95,"context_line":"        \u0027strategy\u0027: ovo_fields.ObjectField(\u0027StrategyPayload\u0027, nullable\u003dTrue),"},{"line_number":96,"context_line":"        \u0027audit_pipeline\u0027: ovo_fields.ObjectField("},{"line_number":97,"context_line":"            \u0027TerseAuditPipelinePayload\u0027, nullable\u003dTrue"},{"line_number":98,"context_line":"        ),"}],"source_content_type":"text/x-python","patch_set":13,"id":"8b8e1196_a4927857","line":95,"updated":"2026-09-01 17:02:40.000000000","message":"see, we coudl condier makign this list in the future.\n\nfor the auit it woudl be a list of one and for the pipeline it woudl be a list of the stragies for each stage in order but we can defer that until its needed for something","commit_id":"85daa4dac35093948644942b412f755cdf30f6ac"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"c6431282fc6bbff749e02705b283defddc8a2010","unresolved":true,"context_lines":[{"line_number":92,"context_line":""},{"line_number":93,"context_line":"    fields \u003d {"},{"line_number":94,"context_line":"        \u0027audit\u0027: ovo_fields.ObjectField(\u0027TerseAuditPayload\u0027, nullable\u003dTrue),"},{"line_number":95,"context_line":"        \u0027strategy\u0027: ovo_fields.ObjectField(\u0027StrategyPayload\u0027, nullable\u003dTrue),"},{"line_number":96,"context_line":"        \u0027audit_pipeline\u0027: ovo_fields.ObjectField("},{"line_number":97,"context_line":"            \u0027TerseAuditPipelinePayload\u0027, nullable\u003dTrue"},{"line_number":98,"context_line":"        ),"}],"source_content_type":"text/x-python","patch_set":13,"id":"eafcfc93_1af745da","line":95,"in_reply_to":"8b8e1196_a4927857","updated":"2026-09-01 22:29:08.000000000","message":"I think that we discussed that during the PTG. But I don\u0027t really see any advantage on keep this info in Action Plans. It should be possible to retrieve it from Audits/AuditPipeline when needed.","commit_id":"85daa4dac35093948644942b412f755cdf30f6ac"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"4f4c41f087230816729e89ed6585622029dc4187","unresolved":false,"context_lines":[{"line_number":92,"context_line":""},{"line_number":93,"context_line":"    fields \u003d {"},{"line_number":94,"context_line":"        \u0027audit\u0027: ovo_fields.ObjectField(\u0027TerseAuditPayload\u0027, nullable\u003dTrue),"},{"line_number":95,"context_line":"        \u0027strategy\u0027: ovo_fields.ObjectField(\u0027StrategyPayload\u0027, nullable\u003dTrue),"},{"line_number":96,"context_line":"        \u0027audit_pipeline\u0027: ovo_fields.ObjectField("},{"line_number":97,"context_line":"            \u0027TerseAuditPipelinePayload\u0027, nullable\u003dTrue"},{"line_number":98,"context_line":"        ),"}],"source_content_type":"text/x-python","patch_set":13,"id":"70717fcc_99ca9d5a","line":95,"in_reply_to":"eafcfc93_1af745da","updated":"2026-09-02 15:22:10.000000000","message":"Acknowledged","commit_id":"85daa4dac35093948644942b412f755cdf30f6ac"}],"watcher/notifications/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":"09a9e5b62d34853123d36f9d048888f1b5998bf3","unresolved":false,"context_lines":[{"line_number":324,"context_line":"    notification.emit(context)"},{"line_number":325,"context_line":""},{"line_number":326,"context_line":""},{"line_number":327,"context_line":"def send_action_notification("},{"line_number":328,"context_line":"    context,"},{"line_number":329,"context_line":"    audit_pipeline,"},{"line_number":330,"context_line":"    action,"}],"source_content_type":"text/x-python","patch_set":4,"id":"772e6110_5977b5a0","line":327,"updated":"2026-08-12 23:03:24.000000000","message":"The commit message states it hooks notifications into PipelineHandler.execute() for start/end/error events, and the send_action_notification function plus execution-start/end/error sample JSONs are added. However, no PipelineHandler class exists anywhere in the codebase, and send_action_notificat...\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: The commit message describes execution notifications as a delivered feature, but they are dead code in production. If this is intentional for a partial implementation (the blueprint is \u0027Partially-implements\u0027), the commit message should not claim the wiring is done.\n\n**Suggestion**:\nEither add the execution notification calls in the pipeline execution path (when PipelineHandler is implemented), or clarify the commit message to indicate that send_action_notification is provided for future use but not yet wired. The current commit message is misleading.","commit_id":"458b21722f4621e22bcb3e3045e952b040de70ce"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"8a4296ef60a1ba4c04c561a6f791665d7096c982","unresolved":false,"context_lines":[{"line_number":324,"context_line":"    notification.emit(context)"},{"line_number":325,"context_line":""},{"line_number":326,"context_line":""},{"line_number":327,"context_line":"def send_action_notification("},{"line_number":328,"context_line":"    context,"},{"line_number":329,"context_line":"    audit_pipeline,"},{"line_number":330,"context_line":"    action,"}],"source_content_type":"text/x-python","patch_set":4,"id":"9d7f93b2_8fb3fa33","line":327,"in_reply_to":"772e6110_5977b5a0","updated":"2026-08-13 15:38:35.000000000","message":"going to be fixed in commit message","commit_id":"458b21722f4621e22bcb3e3045e952b040de70ce"},{"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":"9911dc75c4211942cd2e0e923838b7e20238db76","unresolved":false,"context_lines":[{"line_number":300,"context_line":"    notification.emit(context)"},{"line_number":301,"context_line":""},{"line_number":302,"context_line":""},{"line_number":303,"context_line":"def send_delete("},{"line_number":304,"context_line":"    context, audit_pipeline, stages\u003dNone, service\u003d\u0027infra-optim\u0027, host\u003dNone"},{"line_number":305,"context_line":"):"},{"line_number":306,"context_line":"    \"\"\"Emit an audit_pipeline.delete notification.\"\"\""}],"source_content_type":"text/x-python","patch_set":6,"id":"884b5cac_f43f4c50","line":303,"updated":"2026-08-18 13:32:06.000000000","message":"send_action_notification() and AuditPipelineActionNotification (decorated with the three audit-pipeline-execution-*.json samples) have no caller in the entire tree: grep for \u0027audit_pipeline.send\u0027 and \u0027send_action_notification\u0027 outside tests and the module itself finds only the create/update/delete hooks in watcher/objects/audit_pipeline.py, and no PipelineHandler class exists yet. The commit message is internally inconsistent: it claims \u0027Hook notifications into ... PipelineHandler.execute() (start/end/error)\u0027 while also stating send_action_notification is \u0027not yet wired in this commit\u0027. Consumers reading doc/notification_samples/audit-pipeline-execution-*.json would reasonably expect audit_pipeline.execution.start/end/error events on master, but nothing emits them.\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Impact**: Dead code plus documentation of events that never fire; low runtime risk, but the sample contract is misleading until the pipeline handler lands, and reviewers of the follow-up patch may not realize the events are still dark.\n\n**Recommendation**:\nEither defer adding the execution sample files until the handler wiring patch (matching the stated \u0027not yet wired\u0027 intent), or add a short note to the commit message/sample review confirming the events are emitted by a later patch in the series. If the handler was meant to be wired here as the message claims, add the missing send_action_notification calls.","commit_id":"ca8a370c127333bcbc2fca7b479eb7b2104ced32"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"eb1eef113d3b46031e5610b218aa9fc8cb432760","unresolved":true,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"e9fa2a67_b5e89fc3","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"updated":"2026-08-26 20:47:41.000000000","message":"thinking about this again i think we shoudl remove the terse versan dna dif we are workeid by the skie we can jsut have the stage not be populated if we want a more terse version but we do not need to diffnt classes.\n\njust make it nullable this feels a bit over engeineered\n\nit is what the spec ses but i dont think i tough abou that at any great lenght before","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"a287d533df8a2d9a79d130b0bfb8c326901b5dda","unresolved":false,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"9c75625d_935b753a","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"in_reply_to":"58d18bcf_5c1ff293","updated":"2026-09-03 11:25:57.000000000","message":"as far as im aware the schema and fields shoudl never differ but perhaps your right\n\n\nhttps://github.com/openstack/watcher/blob/master/watcher/notifications/base.py#L83\nand\nhttps://github.com/openstack/nova/blob/master/nova/notifications/objects/base.py#L100-L166\n\ndo work very simmilarly\n\nand tehre are cases in nova where there is drift\n\nhttps://github.com/openstack/nova/blob/master/nova/notifications/objects/instance.py#L26-L120\nhttps://github.com/openstack/nova/blob/master/nova/notifications/objects/request_spec.py#L31-L64\n\n\nif this is a existing pattern in watcher following it is probaly correct for now and we can review the notification in general later.\n\ni think what i was actully thingin of is we have code that generates the notifiction objects form the real objects for some types\n\nhttps://github.com/openstack/nova/blob/master/nova/notifications/objects/image.py#L106-L161\n\nsepcificly our image obejcts\n\nand there we do\n```\n    fields \u003d {\n        k: v.__class__() for k, v in image_meta.ImageMetaProps.fields.items()\n            if k not in (\u0027hw_ephemeral_encryption_secret_uuid\u0027,)}\n\n    SCHEMA \u003d {\n        k: (\u0027image_meta_props\u0027, k) for k in fields}\n```\n\nbut w edont do that everywhere and just because you can often generate the schema form the field does not mean they have to be the same.","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"64078c5560f8421b9164b7417d34519546cf3884","unresolved":true,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"c3a25480_247ff6fe","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"in_reply_to":"58d18bcf_5c1ff293","updated":"2026-09-03 11:35:16.000000000","message":"hmm I don\u0027t know much about the internals of oslo ovos, but don\u0027t see any problem with adding stages to the schema wrt to the `populate_schema` method https://github.com/openstack/watcher/blob/master/watcher/notifications/base.py#L112, why would it not work?","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"296ea051d89793ecfa7d318e1229d6116f2e52fb","unresolved":true,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"b55dc40d_8713c2b9","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"in_reply_to":"5f2017cd_73da1b40","updated":"2026-09-03 12:42:39.000000000","message":"ah ok I see, thanks!","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ba198b73c8b0baf4f0c14531f824e151aa2c156","unresolved":true,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"fc5478cd_9ee0766b","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"in_reply_to":"5f6f4d00_78b15578","updated":"2026-09-01 17:02:40.000000000","message":"yes we should","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"8ff1123da055a482ec53a985370e68ae3385ad63","unresolved":true,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"5f2017cd_73da1b40","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"in_reply_to":"c3a25480_247ff6fe","updated":"2026-09-03 11:51:19.000000000","message":"the populate_schema would work only for scalar types, since it does a raw copy and for \"stages\" we have a list of AuditPipelineStage DB objects to be converted to a ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027) that is being done outside of this method. So we would need to refactor how these things are built for Objects.","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"},{"author":{"_account_id":34452,"name":"Joan Gilabert","display_name":"jgilaber","email":"jgilaber@redhat.com","username":"jgilaber"},"change_message_id":"ca3025505b1a456405580e3539f64a0ad2f08213","unresolved":true,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"5f6f4d00_78b15578","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"in_reply_to":"e9fa2a67_b5e89fc3","updated":"2026-09-01 16:09:14.000000000","message":"I would be ok with keeping the terse version or removing it as suggested, it does not add too much complexity. However, unless I\u0027m missing something, we should add the stages field to the `SCHEMA` if we opt to keep both","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"4d043ed49bf38227a6bd46ffc10119b5974cb861","unresolved":true,"context_lines":[{"line_number":54,"context_line":"        self.populate_schema(stage\u003dstage)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":""},{"line_number":57,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":58,"context_line":"class TerseAuditPipelinePayload(notificationbase.NotificationPayloadBase):"},{"line_number":59,"context_line":"    \"\"\"Pipeline fields without stage detail; used for update/action events.\"\"\""},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    SCHEMA \u003d {"},{"line_number":62,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":63,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":64,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":65,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":66,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":67,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":68,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":69,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":70,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":71,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":72,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":73,"context_line":"    }"},{"line_number":74,"context_line":""},{"line_number":75,"context_line":"    # Version 1.0: Initial version"},{"line_number":76,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":77,"context_line":""},{"line_number":78,"context_line":"    fields \u003d {"},{"line_number":79,"context_line":"        \u0027uuid\u0027: wfields.UUIDField(),"},{"line_number":80,"context_line":"        \u0027name\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":81,"context_line":"        \u0027execution_mode\u0027: ovo_fields.StringField(),"},{"line_number":82,"context_line":"        \u0027state\u0027: ovo_fields.StringField(),"},{"line_number":83,"context_line":"        \u0027audit_type\u0027: ovo_fields.StringField(),"},{"line_number":84,"context_line":"        \u0027auto_trigger\u0027: ovo_fields.BooleanField(),"},{"line_number":85,"context_line":"        \u0027hostname\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":86,"context_line":"        \u0027status_message\u0027: ovo_fields.StringField(nullable\u003dTrue),"},{"line_number":87,"context_line":"        \u0027created_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":88,"context_line":"        \u0027updated_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":89,"context_line":"        \u0027deleted_at\u0027: ovo_fields.DateTimeField(nullable\u003dTrue),"},{"line_number":90,"context_line":"    }"},{"line_number":91,"context_line":""},{"line_number":92,"context_line":"    def __init__(self, audit_pipeline, **kwargs):"},{"line_number":93,"context_line":"        super().__init__(**kwargs)"},{"line_number":94,"context_line":"        self.populate_schema(audit_pipeline\u003daudit_pipeline)"},{"line_number":95,"context_line":""},{"line_number":96,"context_line":""},{"line_number":97,"context_line":"@base.WatcherObjectRegistry.register_notification"},{"line_number":98,"context_line":"class AuditPipelinePayload(TerseAuditPipelinePayload):"},{"line_number":99,"context_line":"    \"\"\"Full pipeline payload including its stages; used for create/delete.\"\"\""},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    SCHEMA \u003d {"},{"line_number":102,"context_line":"        \u0027uuid\u0027: (\u0027audit_pipeline\u0027, \u0027uuid\u0027),"},{"line_number":103,"context_line":"        \u0027name\u0027: (\u0027audit_pipeline\u0027, \u0027name\u0027),"},{"line_number":104,"context_line":"        \u0027execution_mode\u0027: (\u0027audit_pipeline\u0027, \u0027execution_mode\u0027),"},{"line_number":105,"context_line":"        \u0027state\u0027: (\u0027audit_pipeline\u0027, \u0027state\u0027),"},{"line_number":106,"context_line":"        \u0027audit_type\u0027: (\u0027audit_pipeline\u0027, \u0027audit_type\u0027),"},{"line_number":107,"context_line":"        \u0027auto_trigger\u0027: (\u0027audit_pipeline\u0027, \u0027auto_trigger\u0027),"},{"line_number":108,"context_line":"        \u0027hostname\u0027: (\u0027audit_pipeline\u0027, \u0027hostname\u0027),"},{"line_number":109,"context_line":"        \u0027status_message\u0027: (\u0027audit_pipeline\u0027, \u0027status_message\u0027),"},{"line_number":110,"context_line":"        \u0027created_at\u0027: (\u0027audit_pipeline\u0027, \u0027created_at\u0027),"},{"line_number":111,"context_line":"        \u0027updated_at\u0027: (\u0027audit_pipeline\u0027, \u0027updated_at\u0027),"},{"line_number":112,"context_line":"        \u0027deleted_at\u0027: (\u0027audit_pipeline\u0027, \u0027deleted_at\u0027),"},{"line_number":113,"context_line":"    }"},{"line_number":114,"context_line":""},{"line_number":115,"context_line":"    # Version 1.0: Initial version"},{"line_number":116,"context_line":"    VERSION \u003d \u00271.0\u0027"},{"line_number":117,"context_line":""},{"line_number":118,"context_line":"    fields \u003d {"},{"line_number":119,"context_line":"        \u0027stages\u0027: ovo_fields.ListOfObjectsField(\u0027AuditPipelineStagePayload\u0027)"},{"line_number":120,"context_line":"    }"},{"line_number":121,"context_line":""},{"line_number":122,"context_line":"    def __init__(self, audit_pipeline, stages, **kwargs):"},{"line_number":123,"context_line":"        super().__init__("},{"line_number":124,"context_line":"            audit_pipeline\u003daudit_pipeline, stages\u003dstages, **kwargs"},{"line_number":125,"context_line":"        )"},{"line_number":126,"context_line":""},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"@base.WatcherObjectRegistry.register_notification"}],"source_content_type":"text/x-python","patch_set":10,"id":"58d18bcf_5c1ff293","line":125,"range":{"start_line":57,"start_character":0,"end_line":125,"end_character":9},"in_reply_to":"fc5478cd_9ee0766b","updated":"2026-09-02 15:49:29.000000000","message":"This follows the way that was originally implemented. In SCHEMA all fields are copied in populate_schema method (base notification class) as scalar attributes, so those other objects need to stay in fields only. stages are built first and then passed to AuditPipelinePayload constructor.\nIf we want to change current behaviorm, it would required a bigger refactoring on how these notifications are built.","commit_id":"1ab33363b1bf41a23905b75827f5c09cdd68d364"}],"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":"a5d338cb5728c0a6bf5894359148f97d2dc19503","unresolved":false,"context_lines":[{"line_number":161,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    @base.remotable"},{"line_number":164,"context_line":"    def save(self):"},{"line_number":165,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":166,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":167,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":1,"id":"e10cce2e_1faf3177","line":164,"updated":"2026-08-05 19:06:13.000000000","message":"The save() method calls self._from_db_object(self, db_obj) before reading self.old_state. Because _from_db_object sets the state field directly on self, the _obj_state property setter advances old_state to the current post-save value, so the notification always shows old_state \u003d\u003d state.\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: Every audit_pipeline.update notification will report old_state equal to the current state, making the state_update.old_state field useless for tracking actual state transitions. Notification consumers relying on old_state to detect state changes will not see real transitions.\n\n**Priority**: Before merge\n**Why This Matters**: Every audit_pipeline.update notification will report old_state equal to the current state, making the state_update.old_state field useless for tracking actual state transitions. Notification consumers relying on old_state to detect state changes will not see real transitions.\n\n**Recommendation**:\nCapture old_state before calling _from_db_object, e.g.: old_state \u003d self.old_state; self._from_db_object(self, db_obj, eager\u003dFalse); send_update(self._context, self, old_state\u003dold_state). Alternatively, follow the Audit.save() pattern of creating a fresh object for _from_db_object and using obj_refresh().","commit_id":"8e665e1a3e40d56d9da97fc2aa206d77d11625d6"},{"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":"310a77ae199f751e7a97762adf81209fda79b4d5","unresolved":false,"context_lines":[{"line_number":161,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":162,"context_line":""},{"line_number":163,"context_line":"    @base.remotable"},{"line_number":164,"context_line":"    def save(self):"},{"line_number":165,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":166,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":167,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":2,"id":"9a5f3ad5_1f88423d","line":164,"updated":"2026-08-07 20:01:43.000000000","message":"AuditPipeline.save() calls _from_db_object(self, db_obj) before send_update, which re-sets the state field through the _obj_state proxy, advancing _old_state to the current value. As a result, the old_state field in AuditPipelineStateUpdatePayload will always equal state, making state-transition...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: Versioned notification consumers (e.g., telemetry, audit trails) relying on audit_pipeline.update events to detect state transitions will see old_state always equal to state, defeating the purpose of the state_update payload field. The notification sample documents correct behavior that the imple...\n\n**Priority**: Before merge\n**Why This Matters**: Versioned notification consumers (e.g., telemetry, audit trails) relying on audit_pipeline.update events to detect state transitions will see old_state always equal to state, defeating the purpose of the state_update payload field. The notification sample documents correct behavior that the imple...\n\n**Recommendation**:\nCapture old_state before calling _from_db_object. Either: (1) store old_state \u003d self.old_state before the DB refresh and pass it to send_update, or (2) follow the Audit.save() pattern: create a temporary object via self._from_db_object(self.__class__(self._context), db_obj) and use self.obj_refresh(obj) to avoid triggering the state proxy on self.","commit_id":"7989e41162d0f75321cb23b1f6bae2e717069668"},{"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":"310a77ae199f751e7a97762adf81209fda79b4d5","unresolved":false,"context_lines":[{"line_number":175,"context_line":"        current \u003d self.get_by_uuid(self._context, uuid\u003dself.uuid, eager\u003deager)"},{"line_number":176,"context_line":"        self.obj_refresh(current)"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"    @base.remotable"},{"line_number":179,"context_line":"    def soft_delete(self):"},{"line_number":180,"context_line":"        stages \u003d AuditPipelineStage.list("},{"line_number":181,"context_line":"            self._context, filters\u003d{\u0027audit_pipeline_id\u0027: self.id}"}],"source_content_type":"text/x-python","patch_set":2,"id":"4325a421_c66891dd","line":178,"updated":"2026-08-07 20:01:43.000000000","message":"AuditPipeline.soft_delete() calls dbapi.soft_delete_audit_pipeline but does not refresh the in-memory object from the database afterward. Consequently, the deleted_at field in the AuditPipelineDeletePayload notification will be null even though the pipeline was soft-deleted and the DB row has a t...\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Notification consumers expecting the deleted_at timestamp in audit_pipeline.delete events will receive null, causing incorrect audit trail data. The notification sample file documents a non-null deleted_at that the implementation does not produce.\n\n**Suggestion**:\nAfter calling self.dbapi.soft_delete_audit_pipeline(self.uuid), refresh the object from DB (following the Audit.soft_delete() pattern) before sending the delete notification: db_obj \u003d self.dbapi.get_audit_pipeline_by_uuid(self._context, self.uuid); obj \u003d self._from_db_object(self.__class__(self._context), db_obj); self.obj_refresh(obj).","commit_id":"7989e41162d0f75321cb23b1f6bae2e717069668"},{"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":"4472d59786f75c964ca17173cbbdd30b1096096e","unresolved":false,"context_lines":[{"line_number":169,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":170,"context_line":""},{"line_number":171,"context_line":"    @base.remotable"},{"line_number":172,"context_line":"    def save(self):"},{"line_number":173,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":174,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":175,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":3,"id":"6644198e_cb869e83","line":172,"updated":"2026-08-12 13:00:47.000000000","message":"AuditPipeline.save() calls _from_db_object(self, db_obj) which reloads the state field from the DB into the same object instance. This triggers the _obj_state setter a second time, advancing old_state to the current state. As a result, the send_update notification always reports old_state equal t...\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: Every audit_pipeline.update notification produced via AuditPipeline.save() reports old_state \u003d\u003d state, making the state_update payload useless for consumers tracking pipeline state transitions. This also affects soft_delete() which calls save() internally.\n\n**Priority**: Before merge\n**Why This Matters**: Every audit_pipeline.update notification produced via AuditPipeline.save() reports old_state \u003d\u003d state, making the state_update payload useless for consumers tracking pipeline state transitions. This also affects soft_delete() which calls save() internally.\n\n**Recommendation**:\nFollow the Audit.save() pattern: create a new object via self._from_db_object(self.__class__(self._context), db_obj, eager\u003dFalse), call self.obj_refresh(obj), then call send_update. This preserves old_state because obj_refresh only overwrites fields whose values actually differ.","commit_id":"c5e8dbd9e47afd0ae25172e045680e016824427a"},{"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":"4472d59786f75c964ca17173cbbdd30b1096096e","unresolved":false,"context_lines":[{"line_number":184,"context_line":"        self.obj_refresh(current)"},{"line_number":185,"context_line":""},{"line_number":186,"context_line":"    @base.remotable"},{"line_number":187,"context_line":"    def soft_delete(self):"},{"line_number":188,"context_line":"        stages \u003d AuditPipelineStage.list("},{"line_number":189,"context_line":"            self._context, filters\u003d{\u0027audit_pipeline_id\u0027: self.id}"},{"line_number":190,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":3,"id":"ca27de86_a4a7d6ee","line":187,"updated":"2026-08-12 13:00:47.000000000","message":"AuditPipeline.soft_delete() calls dbapi.soft_delete_audit_pipeline() but does not refresh the object from the DB result. Consequently, the delete notification payload has deleted_at\u003dNone instead of the actual soft-delete timestamp. The existing Audit.soft_delete() correctly refreshes from the DB...\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Consumers of the audit_pipeline.delete notification will always see deleted_at as null, losing the timestamp of when the pipeline was soft-deleted. The notification sample file (audit-pipeline-delete.json) shows deleted_at with a timestamp, so the sample does not match actual runtime behavior.\n\n**Suggestion**:\nAfter self.dbapi.soft_delete_audit_pipeline(self.uuid), refresh the object: db_obj \u003d self.dbapi.soft_delete_audit_pipeline(self.uuid); obj \u003d self._from_db_object(self.__class__(self._context), db_obj); self.obj_refresh(obj). Then call send_delete. This matches the Audit.soft_delete() pattern.","commit_id":"c5e8dbd9e47afd0ae25172e045680e016824427a"},{"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":"222e8fc5b2f15651549fc01dd64da6ef559aa6be","unresolved":false,"context_lines":[{"line_number":166,"context_line":"        values \u003d self.obj_get_changes()"},{"line_number":167,"context_line":"        db_obj \u003d self.dbapi.create_audit_pipeline(values)"},{"line_number":168,"context_line":"        self._from_db_object(self, db_obj, eager\u003dTrue)"},{"line_number":169,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":170,"context_line":""},{"line_number":171,"context_line":"    @base.remotable"},{"line_number":172,"context_line":"    def save(self):"}],"source_content_type":"text/x-python","patch_set":5,"id":"92984b54_406ebce5","line":169,"updated":"2026-08-13 20:00:29.000000000","message":"The create() method calls send_create(self._context, self) without passing stages, so the create notification payload always has an empty stages list, even when the pipeline has stages configured.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: Create notifications for pipelines with stages will always show an empty stages array. Consumers relying on create notifications to learn about pipeline stage composition will miss this information until a delete event fires.\n\n**Suggestion**:\nLoad stages before emitting the create notification, mirroring the soft_delete pattern: stages \u003d AuditPipelineStage.list(self._context, filters\u003d{\u0027audit_pipeline_id\u0027: self.id})","commit_id":"80ff8e4d08a44a7b8c14e28d6eb67b6c6d4d937e"},{"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":"222e8fc5b2f15651549fc01dd64da6ef559aa6be","unresolved":false,"context_lines":[{"line_number":169,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":170,"context_line":""},{"line_number":171,"context_line":"    @base.remotable"},{"line_number":172,"context_line":"    def save(self):"},{"line_number":173,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":174,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":175,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":5,"id":"c649a8c6_c190ec74","line":172,"updated":"2026-08-13 20:00:29.000000000","message":"The save() method calls self._from_db_object(self, db_obj) in-place, which re-triggers the _obj_state property setter with the post-save DB value. This causes old_state to be set to the new state rather than the previous state, so state_update in the notification reports old_state \u003d\u003d state.\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Risk**: Consumers of audit_pipeline.update notifications will receive incorrect state transition information. The old_state field will always equal the current state, making it impossible to detect actual state transitions and defeating the purpose of the state_update payload.\n\n**Priority**: Before merge\n**Why This Matters**: Consumers of audit_pipeline.update notifications will receive incorrect state transition information. The old_state field will always equal the current state, making it impossible to detect actual state transitions and defeating the purpose of the state_update payload.\n\n**Recommendation**:\nFollow the same pattern as Audit.save() and ActionPlan.save(): create a separate object instance for _from_db_object, then merge via obj_refresh before sending the notification.","commit_id":"80ff8e4d08a44a7b8c14e28d6eb67b6c6d4d937e"},{"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":"222e8fc5b2f15651549fc01dd64da6ef559aa6be","unresolved":false,"context_lines":[{"line_number":184,"context_line":"        self.obj_refresh(current)"},{"line_number":185,"context_line":""},{"line_number":186,"context_line":"    @base.remotable"},{"line_number":187,"context_line":"    def soft_delete(self):"},{"line_number":188,"context_line":"        stages \u003d AuditPipelineStage.list("},{"line_number":189,"context_line":"            self._context, filters\u003d{\u0027audit_pipeline_id\u0027: self.id}"},{"line_number":190,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":5,"id":"dc58e023_6e0a556c","line":187,"updated":"2026-08-13 20:00:29.000000000","message":"The soft_delete() method does not refresh the object from the database after calling dbapi.soft_delete_audit_pipeline(), so the send_delete notification reports deleted_at\u003dNone despite the record being soft-deleted.\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Consumers of audit_pipeline.delete notifications will see deleted_at\u003dNone, making it appear the pipeline was not actually deleted, inconsistent with the notification sample and with Audit delete notifications.\n\n**Suggestion**:\nRefresh the object from DB after soft-delete, following the Audit.soft_delete() pattern: db_obj \u003d self.dbapi.soft_delete_audit_pipeline(self.uuid); obj \u003d self._from_db_object(...); self.obj_refresh(obj)","commit_id":"80ff8e4d08a44a7b8c14e28d6eb67b6c6d4d937e"},{"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":"9911dc75c4211942cd2e0e923838b7e20238db76","unresolved":false,"context_lines":[{"line_number":184,"context_line":"        self.obj_refresh(current)"},{"line_number":185,"context_line":""},{"line_number":186,"context_line":"    @base.remotable"},{"line_number":187,"context_line":"    def soft_delete(self):"},{"line_number":188,"context_line":"        stages \u003d AuditPipelineStage.list("},{"line_number":189,"context_line":"            self._context, filters\u003d{\u0027audit_pipeline_id\u0027: self.id}"},{"line_number":190,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":6,"id":"3779234d_7360b786","line":187,"updated":"2026-08-18 13:32:06.000000000","message":"AuditPipeline.soft_delete() calls self.dbapi.soft_delete_audit_pipeline(self.uuid) but discards the returned DB row and never refreshes self via _from_db_object/obj_refresh before calling notifications.audit_pipeline.send_delete(). The delete payload\u0027s SCHEMA maps deleted_at (and updated_at) directly from the in-memory object, so the emitted audit_pipeline.delete notification always carries deleted_at: null even though the row is soft-deleted in the database. This diverges from the documented sample doc/notification_samples/audit-pipeline-delete.json (which shows a populated deleted_at) and from the established Audit.soft_delete() pattern in watcher/objects/audit.py, which refreshes the object from the soft-delete result before send_delete().\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: Consumers of versioned notifications (e.g. external billing/audit tooling that uses deleted_at to record deletion time) receive null deleted_at on every audit_pipeline.delete event, and the shipped sample documentation misrepresents the actual payload, undermining the notification contract the samples are meant to define.\n\n**Suggestion**:\nMirror Audit.soft_delete(): capture the return of dbapi.soft_delete_audit_pipeline(self.uuid), refresh self from it (e.g. obj \u003d self._from_db_object(self.__class__(self._context), db_obj, eager\u003dFalse); self.obj_refresh(obj)) before send_delete(), and regenerate/verify audit-pipeline-delete.json against the real emitted payload.","commit_id":"ca8a370c127333bcbc2fca7b479eb7b2104ced32"},{"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":"d7aef9cfeaa1c4b298c59fa9c48fdafb2ca2372f","unresolved":false,"context_lines":[{"line_number":169,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":170,"context_line":""},{"line_number":171,"context_line":"    @base.remotable"},{"line_number":172,"context_line":"    def save(self):"},{"line_number":173,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":174,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":175,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":7,"id":"c6e43906_88632388","line":172,"updated":"2026-08-18 14:13:26.000000000","message":"In AuditPipeline.save(), self._from_db_object(self, db_obj, eager\u003dFalse) is called before send_update(). _from_db_object assigns every field (obj[field] \u003d db_object[field]), which routes \u0027state\u0027 through the _obj_state property setter. That setter always advances the proxy (self._old_state, self._state \u003d self._state, value), so by the time send_update(..., old_state\u003dself.old_state) runs, old_state equals the freshly saved state and the true previous state is lost. The state_update block of every audit_pipeline.update notification therefore shows old_state \u003d\u003d state, and since send_update only populates state when old_state is set, the transition information the feature exists to publish is wrong on every state change.\n\n**Severity**: HIGH | **Confidence**: 0.9\n\n**Impact**: Every audit_pipeline.update notification (including the update emitted during soft_delete) publishes state_update with old_state equal to the new state. Consumers tracking pipeline lifecycle transitions via these versioned notifications get no valid transition data, and the shipped sample documents behavior that is never emitted.\n\n**Priority**: Before merge\n**Recommendation**:\nCapture the transition before refreshing: in save(), read old_state \u003d self.old_state before calling _from_db_object, and pass that captured value to send_update (or mirror watcher/objects/audit.py:322-342 by building a fresh object from the db result and applying it via obj_refresh, which only overwrites differing fields). Then update the assertion in test_pipeline_object_save_emits_update_notification to expect old_state\u003d\u0027PENDING\u0027.","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"227dd7704490c661718b5bbebd66ff77167246ef","unresolved":false,"context_lines":[{"line_number":169,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":170,"context_line":""},{"line_number":171,"context_line":"    @base.remotable"},{"line_number":172,"context_line":"    def save(self):"},{"line_number":173,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":174,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":175,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":7,"id":"d8e3e8d6_037cd2b7","line":172,"in_reply_to":"c6e43906_88632388","updated":"2026-08-19 20:13:06.000000000","message":"Fixed in next PS!","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"},{"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":"d7aef9cfeaa1c4b298c59fa9c48fdafb2ca2372f","unresolved":false,"context_lines":[{"line_number":184,"context_line":"        self.obj_refresh(current)"},{"line_number":185,"context_line":""},{"line_number":186,"context_line":"    @base.remotable"},{"line_number":187,"context_line":"    def soft_delete(self):"},{"line_number":188,"context_line":"        stages \u003d AuditPipelineStage.list("},{"line_number":189,"context_line":"            self._context, filters\u003d{\u0027audit_pipeline_id\u0027: self.id}"},{"line_number":190,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":7,"id":"cb701926_29c1413e","line":187,"updated":"2026-08-18 14:13:26.000000000","message":"AuditPipeline.soft_delete() calls self.dbapi.soft_delete_audit_pipeline(self.uuid) but discards the returned row and does not refresh the in-memory object, then sends the delete notification from the stale object. Because the preceding save() only saw the pre-soft-delete row, the emitted AuditPipelineDeletePayload carries deleted_at: null. This deviates from the established Audit pattern (watcher/objects/audit.py soft_delete refreshes from the returned db_obj via _from_db_object + obj_refresh) and contradicts the committed sample doc/notification_samples/audit-pipeline-delete.json, which documents deleted_at as a populated timestamp.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: The new audit_pipeline.delete notification reports deleted_at as null, giving operators/consumers an incomplete record of the deletion event and a published sample that does not match actual emissions.\n\n**Suggestion**:\nFollow the Audit pattern: capture db_obj \u003d self.dbapi.soft_delete_audit_pipeline(self.uuid), refresh the object from it before sending (self.obj_refresh over a freshly built object, taking care to preserve old_state per CF-001), then call send_delete. Alternatively update the sample to reflect the emitted null value if the omission is intentional.","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"227dd7704490c661718b5bbebd66ff77167246ef","unresolved":false,"context_lines":[{"line_number":184,"context_line":"        self.obj_refresh(current)"},{"line_number":185,"context_line":""},{"line_number":186,"context_line":"    @base.remotable"},{"line_number":187,"context_line":"    def soft_delete(self):"},{"line_number":188,"context_line":"        stages \u003d AuditPipelineStage.list("},{"line_number":189,"context_line":"            self._context, filters\u003d{\u0027audit_pipeline_id\u0027: self.id}"},{"line_number":190,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":7,"id":"93565134_d0d2fe25","line":187,"in_reply_to":"cb701926_29c1413e","updated":"2026-08-19 20:13:06.000000000","message":"Fixed in next PS!","commit_id":"5a4f8cb4275f15b158b23b737634425c567a9968"},{"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":"f6ca5aa4060c9106ea969aa1b2ac96623d3fc44d","unresolved":false,"context_lines":[{"line_number":169,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":170,"context_line":""},{"line_number":171,"context_line":"    @base.remotable"},{"line_number":172,"context_line":"    def save(self):"},{"line_number":173,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":174,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":175,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":9,"id":"0e345aea_dee7bbdf","line":172,"updated":"2026-08-19 23:05:23.000000000","message":"AuditPipeline.save() refreshes a fresh object and copies fields into self via obj_refresh(), but unlike the Audit.save() method it mirrors (watcher/objects/audit.py, which calls self.obj_reset_changes() after notifying), it never resets the change tracker. obj_refresh() applies updates through field assignment, which re-marks fields as changed, and _from_db_object() only resets the change tracker on the throwaway instance, not on self.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: After save(), the object permanently reports pending changes. Any subsequent save() re-persists the stale change set (including updated_at/created_at written back from the loaded copy, which can mask concurrent updates), and code that branches on obj_what_changed() (e.g., delta serialization over RPC or future pipeline flows built on this object) will see phantom dirty fields. The bug is silent today only because the pipeline feature is not yet wired to the API.\n\n**Suggestion**:\nAdd `self.obj_reset_changes()` at the end of AuditPipeline.save(), matching watcher/objects/audit.py Audit.save(). Optionally add an assertion in test_pipeline_object_save_emits_update_notification that `self.pipeline.obj_what_changed() \u003d\u003d set()` after save.","commit_id":"d23c18f9b1afa7c905d700eae8c4a3c5a4942aff"},{"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":"942fe1f13ac66029bb42676db5af73ec1695eeed","unresolved":false,"context_lines":[{"line_number":176,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"    @base.remotable"},{"line_number":179,"context_line":"    def save(self):"},{"line_number":180,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":181,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":182,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":11,"id":"287108bf_eed72a5b","line":179,"updated":"2026-08-27 21:23:13.000000000","message":"AuditPipeline.save() updates the DB, refreshes a throwaway copy via _from_db_object(self.__class__(self._context), db_obj), and emits send_update, but never calls self.obj_reset_changes(). _from_db_object only resets changes on the temporary object it is given, so self stays dirty after every save(). The sibling implementations this patch mirrors (Audit.save() and ActionPlan.save()) both end with self.obj_reset_changes().\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: After save(), obj_get_changes() still returns the saved fields, so a later save() re-writes stale values and re-emits an audit_pipeline.update notification with old_state\u003dNone (state_update block of nulls), producing spurious duplicate events and a perpetually dirty object for anything keyed on obj_what_changed().\n\n**Suggestion**:\nAdd self.obj_reset_changes() at the end of AuditPipeline.save(), matching Audit.save() and ActionPlan.save().","commit_id":"d7357f1e541d1f5e56def7f426afe63010db4e5a"},{"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":"7af88f784bbf4e06f988e4f72b16746945b4e7e6","unresolved":false,"context_lines":[{"line_number":176,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"    @base.remotable"},{"line_number":179,"context_line":"    def save(self):"},{"line_number":180,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":181,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":182,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":12,"id":"b921344a_3ec524a0","line":179,"updated":"2026-08-27 22:11:37.000000000","message":"The rewritten save() deliberately avoids _from_db_object(self, db_obj) (to preserve the _old_state proxy) and instead builds a fresh object plus obj_refresh(obj). However, neither obj_refresh() nor the notification call clears _changed_fields, and the trailing self.obj_reset_changes() present in the analogous Audit.save() (watcher/objects/audit.py) and Action.save() is missing. The previous implementation of save() did reset changes implicitly via _from_db_object(self, ...), so this is a regression introduced by this change.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: After any save(), obj_get_changes()/obj_what_changed() keep reporting previously persisted fields (e.g. state, status_message) forever. Consequences: (1) every later save() re-writes those stale entries to the DB; (2) calling save() again without setting any attribute still performs a DB update and emits a spurious audit_pipeline.update notification with an all-null state_update, misinforming event consumers that count state-change events; (3) any code that branches on obj_what_changed() sees the object as perpetually dirty. Pipeline execution flows that will drive PENDING-\u003eONGOING-\u003eSUCCEEDED transitions through save() hit this on every step.\n\n**Suggestion**:\nAdd `self.obj_reset_changes()` at the end of AuditPipeline.save(), mirroring Audit.save()/Action.save(). Note soft_delete() has the same gap after its obj_refresh(obj) (line 207); adding the reset in save() covers its save() call, but also reset after the soft-delete refresh or verify soft_delete leaves a clean object.","commit_id":"bdcf7bf7d742114d8300b140ebc397b8cf53e73d"},{"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":"a9e5315f08422a2099d8b709fb48f4fe880163a9","unresolved":false,"context_lines":[{"line_number":206,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":207,"context_line":""},{"line_number":208,"context_line":"    @base.remotable"},{"line_number":209,"context_line":"    def save(self):"},{"line_number":210,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":211,"context_line":"        updates.pop(\u0027id\u0027, None)"},{"line_number":212,"context_line":"        db_obj \u003d self.dbapi.update_audit_pipeline(self.uuid, updates)"}],"source_content_type":"text/x-python","patch_set":14,"id":"32e426be_2bb2bef3","line":209,"updated":"2026-09-01 22:24:25.000000000","message":"The rewritten save() builds a separate object, refreshes self via self.obj_refresh(obj), and returns without resetting changes; soft_delete() follows the same pattern. obj_refresh() assigns fields via self[field] \u003d ..., which marks every refreshed field as changed in OVO change-tracking, and nothing clears that state. Both the pre-change implementation (self._from_db_object(self, db_obj, eager\u003dFalse), which calls obj_reset_changes() internally) and the sibling implementations Audit.save() and ActionPlan.save() (which end with an explicit self.obj_reset_changes() after the notification) reset the change set.\n\n**Severity**: WARNING | **Confidence**: 0.85\n\n**Impact**: After any save(), obj_get_changes() on the AuditPipeline object never drains: every subsequent save() re-persists a stale snapshot of all scalar fields. If another worker updates a field (e.g. hostname assigned by the decision engine) between two saves on the same long-lived object instance, the second save silently overwrites it with the stale value read at the first save (lost update). It also deviates from the pattern used by the two sibling objects, inviting copy-paste divergence in future lifecycle methods.\n\n**Suggestion**:\nCall self.obj_reset_changes() after the notification in save() (and at the end of soft_delete(), matching Audit/ActionPlan), i.e. restore the trailing reset that _from_db_object(self, ...) previously provided.","commit_id":"6f40745b58ffbc139e4d2dfddb194f6e04299e97"},{"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":"39c5d992c153aa64b1083de53e35743939d57323","unresolved":false,"context_lines":[{"line_number":211,"context_line":"        self._from_db_object(self, db_obj, eager\u003dTrue)"},{"line_number":212,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":213,"context_line":""},{"line_number":214,"context_line":"    @base.remotable"},{"line_number":215,"context_line":"    def save(self):"},{"line_number":216,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":217,"context_line":"        updates.pop(\u0027id\u0027, None)"}],"source_content_type":"text/x-python","patch_set":15,"id":"d0fdea71_6f676db6","line":214,"updated":"2026-09-02 16:14:35.000000000","message":"The save() method added in this commit updates the DB, refreshes scalar fields via obj_refresh(), and emits send_update(), but never calls self.obj_reset_changes(). The Audit object this code is modeled on ends save() with self.obj_reset_changes() (watcher/objects/audit.py:341). Because _from_db_object() resets only the throwaway object (not self) and obj_refresh() only assigns differing fields (assignment keeps fields marked changed), obj_get_changes() continues to return previously saved fields after save() returns.\n\n**Severity**: WARNING | **Confidence**: 0.8\n\n**Impact**: A long-lived AuditPipeline instance saved more than once re-sends every previously changed field: a stale \u0027state\u0027 or older \u0027updated_at\u0027 can overwrite concurrent updates made by another writer (API service vs decision engine), and duplicate audit_pipeline.update notifications are emitted for already-applied changes.\n\n**Suggestion**:\nAdd self.obj_reset_changes() after the send_update() call in AuditPipeline.save(), matching watcher/objects/audit.py:341. Optionally extend the existing test_save unit test to assert obj_get_changes() is empty after save().","commit_id":"8fd3afbf54a38fa555582d8d743f32fdda2d0612"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"8781209c2689811ac86b6a5505f0734a81ce4a48","unresolved":false,"context_lines":[{"line_number":211,"context_line":"        self._from_db_object(self, db_obj, eager\u003dTrue)"},{"line_number":212,"context_line":"        notifications.audit_pipeline.send_create(self._context, self)"},{"line_number":213,"context_line":""},{"line_number":214,"context_line":"    @base.remotable"},{"line_number":215,"context_line":"    def save(self):"},{"line_number":216,"context_line":"        updates \u003d self.obj_get_changes()"},{"line_number":217,"context_line":"        updates.pop(\u0027id\u0027, None)"}],"source_content_type":"text/x-python","patch_set":15,"id":"f71260b8_1a2ff027","line":214,"in_reply_to":"d0fdea71_6f676db6","updated":"2026-09-02 23:28:31.000000000","message":"Ack, good one","commit_id":"8fd3afbf54a38fa555582d8d743f32fdda2d0612"}],"watcher/tests/unit/notifications/test_audit_pipeline_notification.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":"222e8fc5b2f15651549fc01dd64da6ef559aa6be","unresolved":false,"context_lines":[{"line_number":420,"context_line":"    def test_pipeline_object_soft_delete_emits_delete_notification(self):"},{"line_number":421,"context_line":"        self.pipeline.soft_delete()"},{"line_number":422,"context_line":""},{"line_number":423,"context_line":"        # soft_delete() calls save() (update) then send_delete → 2 info calls."},{"line_number":424,"context_line":"        self.assertEqual(2, self.m_notifier.info.call_count)"},{"line_number":425,"context_line":"        payload \u003d self.m_notifier.info.call_args[1][\u0027payload\u0027]"},{"line_number":426,"context_line":"        # NOTE: send_delete is called after dbapi.soft_delete_audit_pipeline()"}],"source_content_type":"text/x-python","patch_set":5,"id":"b59dcb30_e803e336","line":423,"updated":"2026-08-13 20:00:29.000000000","message":"A comment in test_audit_pipeline_notification.py is 80 characters long, exceeding the project\u0027s 79-character line limit.\n\n**Severity**: SUGGESTION | **Confidence**: 0.9\n\n**Benefit**: Will cause a pep8/flake8 CI failure if E501 is enforced, potentially blocking merge.\n\n**Recommendation**:\nShorten the comment to fit within 79 characters, replacing the arrow character or rephrasing.","commit_id":"80ff8e4d08a44a7b8c14e28d6eb67b6c6d4d937e"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"1ba198b73c8b0baf4f0c14531f824e151aa2c156","unresolved":true,"context_lines":[{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    # ------------------------------------------------------------------"},{"line_number":69,"context_line":"    # Helpers"},{"line_number":70,"context_line":"    # ------------------------------------------------------------------"},{"line_number":71,"context_line":""},{"line_number":72,"context_line":"    def _pipeline_data("},{"line_number":73,"context_line":"        self, state\u003d\u0027PENDING\u0027, updated_at\u003dNone, deleted_at\u003dNone"}],"source_content_type":"text/x-python","patch_set":13,"id":"1e80b924_c90f4566","line":70,"updated":"2026-09-01 17:02:40.000000000","message":"i kind of feel liek we need to docuemtn that this shoudl eb avoid or remvoed\n\nonce this is in the codbase agent will start cargo culting it around more\nand it will lead to review requet ot move helper or other fucntio to relvent section which helps no one in the long run.\n\n\ni have been tempted to do this my self in the past (add section comments)\nbut every tim ei come back to it i rememebr why we dont do it because\nthey get out os sync and cause review agreuemt about wher ecode shoudl live in a file\n\nwhat i have learned is when you are tempted to add a helepr section ectra it often means those fuction coudl live in a seperate moduel or mixing instead.","commit_id":"85daa4dac35093948644942b412f755cdf30f6ac"},{"author":{"_account_id":30002,"name":"Douglas Viroel","email":"viroel@gmail.com","username":"dviroel"},"change_message_id":"c6431282fc6bbff749e02705b283defddc8a2010","unresolved":true,"context_lines":[{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    # ------------------------------------------------------------------"},{"line_number":69,"context_line":"    # Helpers"},{"line_number":70,"context_line":"    # ------------------------------------------------------------------"},{"line_number":71,"context_line":""},{"line_number":72,"context_line":"    def _pipeline_data("},{"line_number":73,"context_line":"        self, state\u003d\u0027PENDING\u0027, updated_at\u003dNone, deleted_at\u003dNone"}],"source_content_type":"text/x-python","patch_set":13,"id":"ad0128f0_513dcb1c","line":70,"in_reply_to":"1e80b924_c90f4566","updated":"2026-09-01 22:29:08.000000000","message":"not sure if you just want me to stop (and tell the agent) adding these sections comments (which I agree in this case, it is not adding any benefit here). OR if you want to move these helper methods to another place... or both :)","commit_id":"85daa4dac35093948644942b412f755cdf30f6ac"},{"author":{"_account_id":11604,"name":"sean mooney","email":"smooney@redhat.com","username":"sean-k-mooney"},"change_message_id":"e12415aea85a1065c4349d59e6d16c305fe79d0f","unresolved":true,"context_lines":[{"line_number":67,"context_line":""},{"line_number":68,"context_line":"    # ------------------------------------------------------------------"},{"line_number":69,"context_line":"    # Helpers"},{"line_number":70,"context_line":"    # ------------------------------------------------------------------"},{"line_number":71,"context_line":""},{"line_number":72,"context_line":"    def _pipeline_data("},{"line_number":73,"context_line":"        self, state\u003d\u0027PENDING\u0027, updated_at\u003dNone, deleted_at\u003dNone"}],"source_content_type":"text/x-python","patch_set":13,"id":"c8a62b26_a55d8613","line":70,"in_reply_to":"ad0128f0_513dcb1c","updated":"2026-09-02 15:24:36.000000000","message":"i think we shoudl have a dicussion about htis and codify it in our docs/agentic cofnig so that we have a consitent style.\n\nfor now i would preer if they were remvoed but this is a mindor thing vs the correctness fo the series\n\nso we could adress that in a follow up\n\ni fwe ousnd help to be gernally useful across files by all means extract them but it noe needed for thsi pto proceed","commit_id":"85daa4dac35093948644942b412f755cdf30f6ac"}]}
