Files
cproof-context/playbooks/add-event-handler.md
jabber.developer2 22977846a3 docs: split context into layered, agent-oriented files
Replace the single file-structure.md with a stratified layout designed
for AI/agent skill consumption: tables and concrete identifiers over
prose, files loaded on demand, content separated by churn rate.

Layers:
- architecture/  stable structural reference (overview, source-map,
                 test-map, data-flow)
- patterns/      memory, commands, autocomplete, events, xmpp,
                 encryption, ui, plugins
- testing/       unit-tests, stubs, functional-tests, bench
- build/         local, docker, ci
- playbooks/     add-command, add-test, add-autocomplete,
                 add-event-handler, add-encryption
- gotchas.md     append-only dated entries (seven seed entries)
- wip/           branch-specific notes; deleted on merge to master

Stable layers describe cproof on master only. In-flight feature
branches (currently feat/ai) get a single file under wip/.

INDEX.md is the entry map with churn labels; SKILL.md is the
always-loaded skill hint pointing to it.
2026-04-30 20:52:06 +03:00

110 lines
3.0 KiB
Markdown

# Playbook: add an event handler
`src/event/` mediates between `src/xmpp/` (and locally-triggered actions)
and the rest of the codebase. Two directions:
- **Server events** (`sv_ev_*`) — triggered by inbound stanzas.
- **Client events** (`cl_ev_*`) — triggered by local actions (UI, command).
See `patterns/events.md` for the conceptual split.
## Decide direction
| Trigger | Direction | File |
|---|---|---|
| Inbound stanza parsed in `src/xmpp/`. | Server. | `src/event/server_events.c/h` |
| User typed a command, UI key, or internal timer. | Client. | `src/event/client_events.c/h` |
## Add the handler — server example
`src/event/server_events.h`:
```c
void sv_ev_foo_received(const char* const from, const char* const payload);
```
`src/event/server_events.c`:
```c
void
sv_ev_foo_received(const char* const from, const char* const payload)
{
// 1. Update persistent state.
chatlog_msg_in(from, payload);
// 2. Push UI update.
ProfChatWin* win = wins_get_chat(from);
if (win) {
chatwin_incoming_msg(win, payload, NULL, FALSE);
} else {
cons_show_incoming_message(from, payload);
}
// 3. Plugin callbacks last — state must be consistent.
plugins_post_chat_message_received(from, payload);
}
```
## Wire the trigger site
For a server event, the matching parser in `src/xmpp/` calls the new
handler. For instance, if `<foo>` stanzas are parsed in
`src/xmpp/message.c`:
```c
// ... after parsing the stanza ...
sv_ev_foo_received(from, payload);
```
For a client event, call from `cmd_funcs.c` (or wherever the user-facing
trigger lives).
## Stub the handler in tests
`tests/unittests/event/stub_*.c` — add a stub for the new function so units
that exercise the trigger site link cleanly:
```c
void
sv_ev_foo_received(const char* const from, const char* const payload)
{
// pass-through (or check_expected, depending on test needs)
}
```
If a test needs to verify the handler is called with specific args, switch
the stub to `check_expected` and the test queues `expect_string` / `expect_value`.
See `testing/stubs.md`.
## Plugin hook (if applicable)
If the event should fire a plugin callback, add a dispatcher in
`src/plugins/plugins.c/h` (e.g. `plugins_post_foo_received`) and call it
**last** in the handler — after state and UI are consistent. Keep C and
Python plugin APIs in sync (`c_api.h` / `python_api.h`).
## Unit test the handler
`tests/unittests/test_server_events.c` (or `test_client_events.c`) holds
event-side tests. Pattern:
```c
void
test_sv_ev_foo_received_writes_chatlog(void** state)
{
expect_string(chatlog_msg_in, jid, "alice@example.com");
expect_string(chatlog_msg_in, msg, "hello");
sv_ev_foo_received("alice@example.com", "hello");
}
```
## Conventions
- One handler, one event. Don't pile multiple unrelated events into the same
function.
- Order inside the handler: state → UI → plugins. Never call plugins before
state is committed.
- `sv_ev_*` and `cl_ev_*` should be straight-line — no XMPP protocol parsing,
no UI rendering. They orchestrate; they don't implement.