Appearance
Event Kinds — Hinata Activity Event Namespace
The /events endpoint (api.michael-engineer.dev/events) ingests structured activity events from across Hinata. Every event has three required string fields plus an optional JSON payload:
| Field | Constraint | Example |
|---|---|---|
source | ≤64 chars · script/script-component name in kebab-case | poll-monzo · hinata-bot-poller · refresh-tasks |
kind | ≤64 chars · domain.action format (lowercase, snake_case action) | monzo.written · bot.reply · task.closed |
summary | ≤240 chars · one-line human-readable description | appended 12 transactions across 2 accounts |
payload | optional JSON object · structured machine-readable detail | {"new_txs": 12, "accounts": 2} |
This file is the canon for the kind namespace. Every new kind added must appear here. If a kind doesn't fit the existing domains, propose a new domain and add it below before shipping the producer.
Naming rules
domain.action— single dot, exactly. No nested domains. No double-dots.- Domain = lowercase, no underscores or hyphens. Single word preferred.
- Action = lowercase, snake_case if multi-word. Verb-shaped (past tense for completed events, present for in-flight).
- No personally identifiable information in
kind— that goes insummaryorpayload. Kinds are for filtering and aggregation, not data carriers. - Domains map roughly to Commanders or major subsystems — but not 1:1 (some domains are cross-cutting like
vault.*andtask.*).
Canonical domains
Data-producer domains (LaunchAgents that pull external data)
| Domain | Owner | Producer scripts | Common kinds |
|---|---|---|---|
monzo | Bulma | poll-monzo.py | monzo.written · monzo.refresh_failed · monzo.token_evicted |
truelayer | Bulma | poll-truelayer.py (TODO) | truelayer.written · truelayer.consent_expiring |
trading212 | Bulma | poll-trading212.py (TODO) | trading212.written · trading212.position_change |
health | AllMight | parse-apple-health.py · health-normaliser.py | health.imported · health.normalised |
fit | AllMight / Zoro | fit-sync.py | fit.synced · fit.weight_logged |
football | Brook | football-pl.py · football-ucl.py | football.fixtures_pulled · football.profile_added |
pilates | Zoro / AllMight | pilates-researcher.py | pilates.class_status · pilates.attended |
mail | Iroh → Dashboard | mail-poller.py · archive-mail-bodies.py | mail.received_priority · mail.archived |
audio | Squidward | transcribe-audio.py | audio.transcribed · audio.transcription_failed |
screen | Hinata | extract-screen-time.py | screen.time_extracted |
Behaviour domains (Michael's actions logged into the system)
| Domain | Owner | Producer | Common kinds |
|---|---|---|---|
smoking | AllMight | update-zoot-state.py · smoking-nudge.py | smoking.zoot_logged · smoking.streak_reset · smoking.nudge_sent · smoking.streak_pb |
fitness | Zoro | update-bodybuilding.py | fitness.session_logged · fitness.pr_set |
music | Squidward | chord chart save · practice timer | music.chart_saved · music.practice_logged |
chart | Squidward | Studio chord-chart UI | chart.saved · chart.exported_html · chart.exported_pdf |
quiz | Shikamaru | Shogi MCQ session events | quiz.round_completed · quiz.score_updated |
nudge | calendar-nudge / smoking-nudge | Telegram outbound | nudge.sent · nudge.suppressed (quiet hours) |
System domains (cross-cutting infrastructure)
| Domain | Owner | Producer | Common kinds |
|---|---|---|---|
task | jimmy-neutron | refresh-tasks.py | task.opened · task.closed · task.auto_closed_by_scan · task.priority_changed |
vault | jimmy-neutron | git hooks (nightly-vault-diff.py + vault.diff_stats retired 2026-06-11 — Michael ruling: handover and transcripts are exhaustive) | vault.commit |
scout | orochimaru | scout-evening.py · scout-midday.py · scout-weekly.py · scout-monthly.py | scout.daily · scout.midday · scout.weekly · scout.monthly |
briefing | jimmy-neutron | morning-briefing.py | briefing.sent |
inbox | canary | normalise-inbox.py · inbox-clear.py | inbox.routed · inbox.assimilated · inbox.discarded · inbox.held · inbox.backlogged |
telegram | hinata-bot-poller | telegram-poller.py | telegram.received · telegram.parse_failed |
bot | hinata-bot-poller | hinata-bot-poller.py | bot.reply · bot.error · bot.token_refresh |
calendar | Iroh | calendar-nudge.py · eod-calendar-sync.py · hinata-cal.py | calendar.event_created · calendar.event_deleted · calendar.day_synced |
studio | trunks | Studio UI events | studio.tab_opened · studio.search_query |
Reserved / never-emit
| Domain | Why |
|---|---|
error | Use [domain].error_* instead — errors stay in their producer's domain so they're queryable alongside successes |
private | If an event would leak credentials or stigma-bearing info, restructure so the payload carries the data redacted, not the kind |
test / debug | Don't emit test events into the prod table; use a --dry-run flag in the producer |
Privacy + payload discipline
- Never include raw credentials, tokens, OAuth secrets in
payload - Financial amounts: pence-integers OK; raw transaction descriptions OK; full card numbers / sort codes never
- Smoking-related payload: number-of-zoots OK; specific brand/dispensary names NOT in summary (PII-style stigma)
- Mail payload: sender + subject OK; full body NEVER
- Calendar payload: title + tag + time OK; meeting attendees can be PII — use
attendee_countnot names
If unsure, omit the field. The kind + summary alone is always queryable.
Adding a new kind — checklist
Before shipping a producer with a new event kind:
- Domain exists in this file? If yes, just pick an action name. If no, propose the new domain here first (one PR).
- Action name is verb-shaped?
written,synced,received,closed— yes.transactions,data,stuff— no. - No PII in kind itself? The kind is searchable; don't burn sensitive info into the literal namespace.
- Has a
summarythat reads correctly in the Activity Log? Imagine seeing the kind+summary 30 days later — would you know what happened? - Payload is structured (object), not a string? Strings can't be queried; nested objects let consumers extract specific keys.
- Producer wraps emit_event in try/except? Endpoint downtime must never break the producer.
Migration note — existing events
These kinds were emitted before this glossary existed. They're already in the events table; new producers should follow the glossary going forward and any in-flight migrations align here.
monzo.written✓ (matches)health.imported✓ (matches)nudge.sent✓ (matches)
See also
- event-driven-architecture (TODO doctrine)
- credential-guardrails (extension applies to event payloads)
- 2026-05-api-connection-opportunities — the parent project this glossary unblocks