feat(profiler/behave_shell): stamp attacker_uuid on bus payload (Phase 5 prep)

The profiler worker's per-observation publish now re-merges
attacker_uuid into the bus payload alongside id/ts/v. Same shape as
the existing DECNET-side deviation from BEHAVE's wire-format
docstring (BEHAVE-INTEGRATION.md §339-366) — widens the deviation
by one DECNET denorm field.

Phase 5's per-attacker SSE route can now filter
attacker.observation.* events to one attacker in O(1) without a repo
round-trip per event. identity_ref stays None today (until the
attribution engine ships); attacker_uuid is independent.

Two test changes:
* test_happy_path_persists_and_publishes asserts attacker_uuid is in
  every published payload.
* New test_attacker_uuid_in_payload_for_filter pins the field
  explicitly and confirms it doesn't conflate with identity_ref.
This commit is contained in:
2026-05-08 20:18:32 -04:00
parent 5ff89eefe7
commit 5116023bf7
2 changed files with 35 additions and 4 deletions

View File

@@ -96,15 +96,23 @@ def _flatten_observation(obs: Observation, attacker_uuid: str) -> dict[str, Any]
}
def _publish_observation(publish: Optional[PublishFn], obs: Observation) -> None:
"""Best-effort publish; never raise. Re-merges id/ts/v into payload
per BEHAVE-INTEGRATION.md §339-366 deviation note."""
def _publish_observation(
publish: Optional[PublishFn],
obs: Observation,
attacker_uuid: str,
) -> None:
"""Best-effort publish; never raise. Re-merges id/ts/v plus
DECNET-side ``attacker_uuid`` denorm into payload per
BEHAVE-INTEGRATION.md §339-366 deviation note. The ``attacker_uuid``
stamp gives the per-attacker SSE route an O(1) filter without a
repo round-trip per event (Phase 5)."""
if publish is None:
return
payload = to_event_payload(obs) | {
"id": obs.id,
"ts": obs.ts,
"v": obs.v,
"attacker_uuid": attacker_uuid,
}
try:
publish(event_topic_for(obs.primitive), payload, obs.primitive)
@@ -202,7 +210,7 @@ async def handle_session_ended(
# 7. Publish — fire-and-forget, never raises out.
for obs in observations:
_publish_observation(publish, obs)
_publish_observation(publish, obs, attacker_uuid)
log.info(
"behave_handler: persisted=%d primitives sid=%s attacker_ip=%s",