Skip to content

[GL-1] Canonical Message Envelope + Log Event Framework #1

@benevolentbandwidth-nurtekin

Description

Stream A — Foundation

Labels: core, foundation
Depends on: Nothing
Effort: 4–5 hours

Summary

The single most critical file. Every other component depends on it. Defines the universal message format all channels normalize to AND the structured log event format that gets exported to GiveLight. Two contracts in one file.

What to Build

File: app/core/envelope.py

CanonicalMessage fields: session_id (SHA-256 hash, never raw phone), channel, input_type (text/image/audio/document/location), text_content, media_url (temporary, never persisted), language_hint (ISO 639-1), location_context (country+region only, never coordinates), session_context, submission_type (intake | verification_evidence | monitoring_checkin | unknown), prior_context (in-memory only), timestamp.

LocationContext: country_code (ISO 3166-1), region, city (best-effort), source (device | prompt | phone_prefix | none), confidence (0.0–1.0). Coordinates are NEVER stored in this dataclass — the fields do not exist.

LogEvent: event_type (enum), session_id_hash, timestamp, cleartext_payload (dict), pii_envelope (bytes | None — populated by the encryption layer in GL-20), agent_version, platform_version, schema_version. LogEvent.emit() appends to the in-memory log buffer on the Session object.

make_session_id(channel_user_id, channel) -> str  # SHA-256, rotates daily

Privacy Contract

  • make_session_id() uses SHA-256. Same user same day = same hash. Next day = different hash.
  • Coordinates never stored in any field. LocationContext has no lat/lng fields.
  • to_agent_prompt() never includes session_id, phone, channel name, or coordinates.
  • LogEvent.cleartext_payload never contains raw phone, name, or coordinates.
  • LogEvent.pii_envelope is opaque bytes — set by encryption layer, never inspected here.

Acceptance Criteria

  • All dataclasses with type hints and docstrings.
  • make_session_id() is SHA-256, consistent within a day, different next day.
  • submission_type enum covers all 4 values, defaults to unknown.
  • LogEvent.emit() appends to session-scoped in-memory buffer.
  • Unit tests: hash consistency, daily rotation, prompt variants, log event emission.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions