)]}'
{"/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":"18c3acaa24738d2b2b461e6160324e47103151b3","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     b7534e55 (Add Ironic emulator and saving_energy functional tests)"},{"line_number":2,"context_line":"Author:     jgilaber \u003cjgilaber@redhat.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-03-19 17:16:16 +0100"},{"line_number":4,"context_line":"Commit:     jgilaber \u003cjgilaber@redhat.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":3,"id":"d303a49c_7d8bbe3e","line":1,"updated":"2026-09-16 17:15:37.000000000","message":"The commit body states \u0027Move usage of ironicclient in the maas_helper to IronicHelper.\u0027 The commit only moves ironicclient usage in watcher/common/metal_helper/ironic.py (the ironic metal helper); the MaaS helper (watcher/common/metal_helper/maas.py) uses python-libmaas, contains no ironicclient usage, and no maas_helper module exists anywhere in the repository.\n\n**Severity**: SUGGESTION | **Confidence**: 0.85\n\n**Impact**: Permanent change history directs future readers verifying this refactor to the MAAS helper; someone auditing when ironicclient usage left the MAAS path, or confirming which helper was refactored, will find no matching change and may doubt the history.\n\n**Recommendation**:\nAmend the sentence to name the ironic metal helper, e.g. \u0027Move usage of ironicclient in the metal_helper to IronicHelper\u0027, before the change merges.","commit_id":"a9993136ba2145b89ee7da92472cc677f43ee1c4"},{"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":"d40bc5823f3df7e80dfe8cfe3e2aab47d108659b","unresolved":false,"context_lines":[{"line_number":1,"context_line":"Parent:     0b1bfb40 (Add Ironic emulator and saving_energy functional tests)"},{"line_number":2,"context_line":"Author:     jgilaber \u003cjgilaber@redhat.com\u003e"},{"line_number":3,"context_line":"AuthorDate: 2026-03-19 17:16:16 +0100"},{"line_number":4,"context_line":"Commit:     jgilaber \u003cjgilaber@redhat.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":5,"id":"9445ad06_709bfdb4","line":1,"updated":"2026-09-17 10:51:43.000000000","message":"The commit message states \u0027Move usage of ironicclient in the maas_helper to IronicHelper\u0027, but no MAAS helper code is touched by this commit. The actual change moves ironicclient usage out of the metal_helper\u0027s ironic wrapper (watcher/common/metal_helper/ironic.py) into watcher.common.ironic_helper.IronicHelper. The MAAS helper (watcher/common/metal_helper/maas.py) uses python-libmaas, not ironicclient, and is unmodified, so the sentence describes a change that does not exist.\n\n**Severity**: WARNING | **Confidence**: 0.88\n\n**Impact**: The permanent change history misdirects maintainers: anyone auditing MAAS code paths to understand, verify, or revert this change will find nothing, and future readers may wrongly conclude MAAS power-state behavior was altered when only the Ironic helper path was refactored.\n\n**Suggestion**:\nWhen amending, reword the sentence to name the metal_helper, e.g. \u0027Move usage of ironicclient in the metal_helper to IronicHelper\u0027. The remaining message paragraphs accurately describe the IronicNode dataclass and the node.get Struct-to-string fix.","commit_id":"515ff7507c5e99e329990934b83bdc44aea26c53"}],"watcher/common/ironic_helper.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":"b49024ec172d0fad8e6d74633eab59e30321e9b9","unresolved":false,"context_lines":[{"line_number":71,"context_line":"            for node in self.ironic.node.list()"},{"line_number":72,"context_line":"        ]"},{"line_number":73,"context_line":""},{"line_number":74,"context_line":"    def get_ironic_node_by_uuid(self, node_uuid: str) -\u003e None:"},{"line_number":75,"context_line":"        \"\"\"Get ironic node by node UUID\"\"\""},{"line_number":76,"context_line":"        try:"},{"line_number":77,"context_line":"            node \u003d self.ironic.node.get(node_uuid)"}],"source_content_type":"text/x-python","patch_set":2,"id":"6c9b3ec6_cdee74ad","line":74,"updated":"2026-07-31 11:00:43.000000000","message":"The method get_ironic_node_by_uuid is annotated with return type -\u003e None, but it returns an IronicNode object (IronicNode.from_ironicclient(node)) on the final line. The annotation should be -\u003e IronicNode to match the actual return value and the pattern used by get_ironic_node_list which is corre...\n\n**Severity**: WARNING | **Confidence**: 0.9\n\n**Impact**: Any static type checker (mypy, pyright) will report errors at call sites that use the return value as an IronicNode. Developers relying on the annotation will be misled into thinking the method returns None.\n\n**Suggestion**:\nChange the return type annotation from -\u003e None to -\u003e IronicNode to match the actual return value.","commit_id":"f0ac21feabe598efd1e91ae679ba0493f9552bc8"},{"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":"18c3acaa24738d2b2b461e6160324e47103151b3","unresolved":false,"context_lines":[{"line_number":71,"context_line":"            for node in self.ironic.node.list()"},{"line_number":72,"context_line":"        ]"},{"line_number":73,"context_line":""},{"line_number":74,"context_line":"    def get_ironic_node_by_uuid(self, node_uuid: str) -\u003e None:"},{"line_number":75,"context_line":"        \"\"\"Get ironic node by node UUID\"\"\""},{"line_number":76,"context_line":"        try:"},{"line_number":77,"context_line":"            node \u003d self.ironic.node.get(node_uuid)"}],"source_content_type":"text/x-python","patch_set":3,"id":"14d18f35_7ff850e3","line":74,"updated":"2026-09-16 17:15:37.000000000","message":"The changed signature declares \u0027-\u003e None\u0027, yet the method ends with \u0027return IronicNode.from_ironicclient(node)\u0027. The annotation misstates the interface this change introduces: the sibling get_ironic_node_list correctly annotates \u0027list[IronicNode]\u0027, and both the metal helper and the updated unit tests rely on receiving an IronicNode instance.\n\n**Severity**: WARNING | **Confidence**: 0.95\n\n**Impact**: Static type checkers and IDEs treat every use of the return value as accessing an attribute of None, and developers reading the signature may discard or guard against the result, leading to avoidable rework or incorrectly \u0027fixed\u0027 call sites as this wrapper becomes the standard ironic access path.\n\n**Suggestion**:\nChange the return annotation of get_ironic_node_by_uuid from None to IronicNode.","commit_id":"a9993136ba2145b89ee7da92472cc677f43ee1c4"}],"watcher/tests/unit/common/utils.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":"b49024ec172d0fad8e6d74633eab59e30321e9b9","unresolved":false,"context_lines":[{"line_number":306,"context_line":"            \u0027uuid\u0027: kwargs.pop(\u0027uuid\u0027, w_utils.generate_uuid()),"},{"line_number":307,"context_line":"            \u0027power_state\u0027: kwargs.pop(\u0027power_state\u0027, \u0027power on\u0027),"},{"line_number":308,"context_line":"            \u0027maintenance\u0027: kwargs.pop(\u0027maintenance\u0027, False),"},{"line_number":309,"context_line":"            \u0027maintenance_reason\u0027: kwargs.pop(\u0027maintenance_reason\u0027, \"None\"),"},{"line_number":310,"context_line":"            \u0027extra\u0027: kwargs.pop(\u0027extra\u0027, {\u0027compute_node_id\u0027: \u00271\u0027}),"},{"line_number":311,"context_line":"        }"},{"line_number":312,"context_line":"        node_info.update(kwargs)"}],"source_content_type":"text/x-python","patch_set":2,"id":"b0b68791_f824ee87","line":309,"updated":"2026-07-31 11:00:43.000000000","message":"The create_ironicclient_node test helper sets the default maintenance_reason to the string \"None\" rather than Python None. The IronicNode dataclass types maintenance_reason as str | None, and real ironic API responses use JSON null (deserialized to Python None) when a node is not in maintenance....\n\n**Severity**: SUGGESTION | **Confidence**: 0.8\n\n**Benefit**: Tests using the default maintenance_reason will not accurately represent production data where maintenance_reason is None (not the string \"None\") when a node is not in maintenance. Any future test asserting maintenance_reason is None will fail unless the caller explicitly passes None.\n\n**Recommendation**:\nChange the default from \"None\" to None so the test helper faithfully represents the data shape returned by the ironic API.","commit_id":"f0ac21feabe598efd1e91ae679ba0493f9552bc8"}]}
