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.
This commit is contained in:
114
gotchas.md
Normal file
114
gotchas.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Gotchas
|
||||
|
||||
Append-only log of known pitfalls. One entry per pitfall. Format:
|
||||
|
||||
```
|
||||
## YYYY-MM-DD — <short title>
|
||||
|
||||
<body — what bites, why, what to do instead>
|
||||
```
|
||||
|
||||
Scan this file before finishing any non-trivial task.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — `g_strsplit` result must use `g_strfreev`
|
||||
|
||||
`g_strsplit` (and `g_strdupv`) return a `gchar**` whose elements are
|
||||
individually GLib-allocated. Free with `g_strfreev`, **not** `g_free`. The
|
||||
latter compiles fine and silently leaks the inner strings.
|
||||
|
||||
The cleanup macro is `auto_gcharv` (declared in `src/common.h`):
|
||||
|
||||
```c
|
||||
auto_gcharv gchar** parts = g_strsplit(line, " ", -1);
|
||||
```
|
||||
|
||||
Mixing `g_free` on a `gchar**` is the most common allocator-mismatch bug in
|
||||
this codebase.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — `JABBER_CONNECTED` must be queued in unit tests
|
||||
|
||||
Most XMPP-touching code paths gate on `connection_get_status()`. The unit-
|
||||
test stub returns `mock()`, so every test reaching that gate must:
|
||||
|
||||
```c
|
||||
will_return(connection_get_status, JABBER_CONNECTED);
|
||||
// or
|
||||
will_return(connection_get_status, JABBER_DISCONNECTED);
|
||||
```
|
||||
|
||||
Forgetting this aborts cmocka with a "missing mock value" error. If you see
|
||||
that error, it's almost always `connection_get_status` you forgot.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — Autocomplete callbacks must be stateless
|
||||
|
||||
Some older callbacks keep "last match" state in a file-static `_last_*_match`
|
||||
variable. Do not copy that pattern. A clean canonical example is
|
||||
`roster_contact_autocomplete` (`src/xmpp/roster_list.c`) — it delegates
|
||||
straight to `autocomplete_complete` against a roster-owned `Autocomplete`
|
||||
object and keeps no callback-local state.
|
||||
|
||||
Stateful callbacks misbehave under shift-tab cycling and break when two
|
||||
completers run concurrently in a layered command (e.g. `/foo set bar <tab>`
|
||||
followed by `/foo set baz <tab>`).
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — CWE-134 format-string discipline
|
||||
|
||||
Never pass user-controlled or arbitrary strings as the format argument to
|
||||
`printf`-family or GLib formatted-print functions. A recent compiler-
|
||||
hardening pass (cf. cproof commit `9ec01fa8c`) audited and tightened these
|
||||
sites; do not regress.
|
||||
|
||||
Right:
|
||||
|
||||
```c
|
||||
cons_show("%s", user_text);
|
||||
```
|
||||
|
||||
Wrong:
|
||||
|
||||
```c
|
||||
cons_show(user_text);
|
||||
```
|
||||
|
||||
The compiler does not always warn, especially across helper layers. Enable
|
||||
and respect `-Wformat -Wformat-security` warnings in any build you trust.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — Build inside Docker, not on the host
|
||||
|
||||
Library version drift between a developer host and `Dockerfile.debian`
|
||||
(GLib, OpenSSL, libstrophe in particular) can mask or invent failures. CI
|
||||
is the source of truth; CI builds inside the Debian image. See
|
||||
`build/docker.md`.
|
||||
|
||||
If you absolutely must build on the host, mirror the package set in
|
||||
`Dockerfile.debian` exactly and pin versions — you will spend more time
|
||||
chasing version drift than the Docker build would have cost.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — Beware of "Profanity" vs "cproof" naming
|
||||
|
||||
The fork-rename is incomplete. Many files, symbols, copyright headers, and
|
||||
config keys still say `Profanity` / `prof_` / `profanity`. These are
|
||||
authoritative as-is; do **not** rename them opportunistically alongside
|
||||
unrelated changes. Rename work, when it happens, will be a dedicated PR.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-30 — Encryption disable builds must compile
|
||||
|
||||
Each encryption stack (`HAVE_OMEMO`, `HAVE_LIBOTR`, `HAVE_LIBGPGME`) can be
|
||||
turned off at `configure` time. New code that calls into these modules
|
||||
must wrap calls in the matching `#ifdef` and provide a graceful fallback
|
||||
in the `#else`. CI does not always test every disable combination — verify
|
||||
locally with at least the off-build for each stack you touched.
|
||||
Reference in New Issue
Block a user