Files
video-setup/conventions.md
mikael-lovqvists-claude-agent 1066f793e2 docs: sync docs with code; fix Makefile modules target
Makefile:
- Add reconciler and ingest to the `modules` target; they were only built
  as side-effects of `make node`, making `make modules` incomplete

planning.md:
- Add 4 missing CLI drivers: discovery_cli, config_cli, protocol_cli,
  query_cli (all existed in code and dev/cli/Makefile but were absent)
- Add header-only utilities table: stream_stats.h, v4l2_fmt.h

README.md:
- Add transport_cli, discovery_cli, config_cli, protocol_cli, query_cli
  to CLI tools list

conventions.md:
- Add ERR_NOT_FOUND to Error_Code enum example
- Replace placeholder Invalid_Error_Detail with actual fields
  (config_line, message) that have been in use since config module
- Add missing error macros: APP_INVALID_ERROR, APP_INVALID_ERROR_MSG,
  APP_NOT_FOUND_ERROR
- Update directory structure: node/ description (was "later"), add web/
  and tools/ entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 21:55:43 +00:00

5.5 KiB

Conventions and Preferences

Language

  • C11 throughout (-std=c11)
  • _GNU_SOURCE defined via -D_GNU_SOURCE in all Makefiles — enables the full GNU/Linux feature set (POSIX 2008, BSD, GNU and Linux-specific extensions); do not define this in source files
  • LTO enabled via -flto in all Makefiles — the compiler handles inlining across translation units at link time; do not use static on functions solely to enable inlining
  • Target platform: Linux (V4L2, epoll, etc. are Linux-specific)

Naming

Kind Convention Example
Structs Title_Snek_Case struct App_Error, struct Frame_Header
Enums (type) Title_Snek_Case enum Error_Code
Enum values CAPITAL_SNEK_CASE ERR_NONE, ERR_SYSCALL
Functions lower_snek_case v4l2_ctrl_open(), app_error_print()
Local variables lower_snek_case frame_count, device_fd
Constants (#define, const) CAPITAL_SNEK_CASE MAX_FRAME_SIZE
Module-level singletons lower_snek_case (treated as functions)

Identifiers are prefixed with their module name where disambiguation is needed (e.g. v4l2_ctrl_, media_ctrl_, transport_).


Types

No typedefs for structs

Always use the struct keyword at the call site. This makes composite types unambiguous without inspecting declarations.

/* correct */
struct App_Error err = app_error_ok();
void process(struct Frame_Header *header);

/* wrong */
typedef struct { ... } App_Error;

typedefs are acceptable for

  • Enums: typedef enum Error_Code { ... } Error_Code; — allows using the name without enum keyword
  • Scalar aliases where the underlying type is genuinely irrelevant to the caller (e.g. typedef uint32_t Frame_Id;)

Formatting

  • Indentation: tabs (display width 4)
  • Braces: always use block braces, including single-statement bodies
/* correct */
if (err.code != ERR_NONE) {
    return err;
}

/* wrong */
if (err.code != ERR_NONE)
    return err;
  • Quotes: double quotes for string literals (C standard)

Error Handling

Errors are returned as struct App_Error values. Functions that can fail return struct App_Error directly, or take an out-parameter for the result alongside it.

Structure

/* modules/common/error.h */

typedef enum Error_Code {
    ERR_NONE      = 0,
    ERR_SYSCALL   = 1,   /* errno is meaningful */
    ERR_INVALID   = 2,
    ERR_NOT_FOUND = 3,
} Error_Code;

struct Syscall_Error_Detail {
    int err_no;
};

struct Invalid_Error_Detail {
    int         config_line;   /* source line number, or 0 if not applicable */
    const char *message;       /* static string describing what was wrong     */
};

struct App_Error {
    Error_Code  code;
    const char *file;   /* __FILE__ — future: replaced by generated location code */
    int         line;   /* __LINE__ — future: replaced by generated location code */
    union {
        struct Syscall_Error_Detail  syscall;
        struct Invalid_Error_Detail  invalid;
    } detail;
};

Macros

#define APP_OK \
    ((struct App_Error){ .code = ERR_NONE })

#define APP_IS_OK(e) \
    ((e).code == ERR_NONE)

#define APP_SYSCALL_ERROR() \
    /* sets ERR_SYSCALL + captures errno */

#define APP_INVALID_ERROR() \
    /* sets ERR_INVALID, no message */

#define APP_INVALID_ERROR_MSG(cfg_line, msg) \
    /* sets ERR_INVALID with config_line and message */

#define APP_NOT_FOUND_ERROR() \
    /* sets ERR_NOT_FOUND */

Presentation

Each error kind is handled in app_error_print(), which writes a human-readable description to stderr. A JSON writer (app_error_write_json()) will be added later and will use the same structured fields.

Future upgrade

When the custom preprocessor is available, __FILE__ and __LINE__ will be replaced by a generated numeric location code that uniquely identifies the error site in the project. JSON consumers will see the same struct shape either way.


Directory Structure

video-setup/
  src/                  - all C source files
    modules/            - translation units; each has a .c and a Makefile
      common/           - shared types (error, base definitions); no external dependencies
      <module>/
    node/               - video node binary (source + display sink roles)
  include/              - public headers (.h files)
  dev/                  - development aids; not part of the final deliverable
    cli/                - exploratory CLI drivers, one per module
    web/                - development web UI (Node.js/Express)
    experiments/        - freeform experiments
  tools/                - build-time code generators (e.g. gen_font_atlas)
  tests/                - automated tests (later)
  Makefile              - top-level build

Source files live in src/. Public headers live in include/. CLI drivers and experiments live in dev/. Nothing in dev/ is a dependency of anything in src/.


Module Structure

Each module has its source under src/modules/<name>/ and its public header under include/:

src/modules/<name>/
  <name>.c     - implementation
  Makefile     - builds a static object

include/
  <name>.h     - public API; minimal includes, no implementation details

The corresponding CLI driver lives at dev/cli/<name>_cli.c.


Build

  • GNU make with tabs for recipe indentation
  • Each module builds to a static object (.o)
  • CLI drivers link the module object(s) they need
  • No CDN or vendored sources; dependencies are system libraries (libc, Linux kernel headers)