Skip to content

FIX Preserve lifecycle on result persistence failures - #2473

Open
Roman Lutz (romanlutz) wants to merge 3 commits into
microsoft:mainfrom
romanlutz:romanlutz-daily-audit-2026-08-23
Open

FIX Preserve lifecycle on result persistence failures#2473
Roman Lutz (romanlutz) wants to merge 3 commits into
microsoft:mainfrom
romanlutz:romanlutz-daily-audit-2026-08-23

Conversation

@romanlutz

Copy link
Copy Markdown
Contributor

Description

Attack result persistence previously ran in a best-effort event handler. If the database write failed, the exception was ignored and the caller could receive a completed result even though no durable record existed for reporting or resume.

This change keeps generic lifecycle observers best-effort, enriches results during ON_POST_EXECUTE, and persists completed results only after teardown succeeds. Persistence failures now propagate without creating a misleading second ERROR result, while genuine execution or teardown failures continue to persist one accurate ERROR result and preserve the original exception.

N/A

Tests and Documentation

  • Added regression coverage for post-teardown persistence failures, partial commits, duplicate prevention, teardown failures, and observer failures.
  • Ran 61 focused strategy tests and 2,243 executor/scenario tests.
  • Ran the changed-file pre-commit hooks, including Ruff and scoped type checking.
  • Documentation changes were not applicable.

JupyText was not applicable because no documentation notebooks changed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3772242b-0ee1-4ecd-a028-a38c91c17926
Persist completed attack results after teardown without making generic event observers fatal or creating duplicate error results.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 75013b84-0c8d-4ea4-9c4c-6c58381ac1a1
@hannahwestra25 hannahwestra25 self-assigned this Aug 24, 2026
Comment on lines +628 to +641
async def execute_with_context_async(self, *, context: AttackStrategyContextT) -> AttackStrategyResultT:
"""
Execute an attack and persist its completed result after teardown.

Args:
context (AttackStrategyContextT): The attack execution context.

Returns:
AttackStrategyResultT: The completed and persisted attack result.
"""
result = await super().execute_with_context_async(context=context)
self._default_event_handler._persist_result(result=result)
return result

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cp pointed out that this works if there's no error but if the attack & db write fail, the db error doesn't get saved.

so we'd need something like this:

try:
    result = await super().execute_with_context_async(context=context)
except Exception as attack_error:
    # build_error_result would use _on_error_async()
    error_result = self._default_event_handler._build_error_result(
        context=context,
        error=attack_error.__cause__ or attack_error,
    )
    try:
        self._default_event_handler._persist_result(result=error_result)
    except Exception as database_error:
        raise ExceptionGroup(
            "Attack and result persistence failed",
            [attack_error, database_error],
        )
    raise

self._default_event_handler._persist_result(result=result)
return result

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants