Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/scheduled_tasks.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"sessionId":"f1eb13be-b61a-4f8b-bd22-8ae7afb8a969","pid":42206,"acquiredAt":1773091471492}
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,3 @@ bin_wrap/
/patch_zig_main.sh
/hi.cel
lang/swift/.build/

# ── macOS SDK overlay (generated by tools/patch-sdk.sh) ──────────────
.darwin-sdk-overlay/
2 changes: 1 addition & 1 deletion .zigversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.16.0-dev.2984+cb7d2b056
0.16.0-dev.2979+e93834410
185 changes: 2 additions & 183 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Guidance for AI coding agents working in this repository.
- **Test**: `zig build test --summary all` or `./build.sh test --summary all`
- **Lint**: `zig build lint` / **Fix**: `zig build fix`
- **Parity check**: `zig build check-parity`
- **Validation gates**: `zig build feature-tests`, `mcp-tests`, `messaging-tests`, `secrets-tests`, `pitr-tests`, `agents-tests`, `orchestration-tests`, `gateway-tests`, `inference-tests`, `cli-tests`, `tui-tests`, `typecheck` (compile-only), `validate-flags`, `full-check`, `verify-all`
- **Full gate**: `zig build check` (lint + test + parity)
- **CLI**: `zig build cli` produces `zig-out/bin/abi`
- **MCP server**: `zig build mcp` produces `zig-out/bin/abi-mcp`
Expand All @@ -26,49 +27,10 @@ Guidance for AI coding agents working in this repository.
9. `var` vs `const`: compiler enforces const for never-mutated locals.
10. On macOS 26.4+, use `./build.sh` instead of `zig build` (LLD linker issue).

## Build Commands

### Full builds and gates
```bash
./build.sh # Build (macOS 26.4+ auto-relinks with Apple ld)
zig build test --summary all # Unit + integration tests
zig build lint # Check formatting (read-only)
zig build fix # Auto-format in place
zig build check # Lint + test + parity (full gate)
zig build check-parity # Verify mod/stub declaration parity
```

### Running single tests
```bash
zig build test --summary all -- --test-filter "test_name_pattern"
./build.sh test --summary all -- --test-filter "test_name_pattern"
```

### Focused test lanes
```bash
zig build feature-tests # Feature integration + parity tests
zig build messaging-tests # Messaging unit + integration
zig build agents-tests # Agents unit + integration
zig build orchestration-tests # Orchestration unit + integration
zig build gateway-tests # Gateway unit + integration
zig build inference-tests # Inference unit + integration
zig build secrets-tests # Secrets unit + integration
zig build pitr-tests # PITR unit + integration
zig build mcp-tests # MCP integration tests
zig build cli-tests # CLI tests
zig build tui-tests # TUI tests
zig build typecheck # Compile-only for current target
zig build cross-check # Cross-compilation (linux, wasi, x86_64)
```

### Platform linking
- **macOS**: use `linkIfDarwin()` from `build/linking.zig` instead of inline `if (os.tag == .macos)` checks. 13 callsites consolidated.
- On macOS 26.4+: always use `./build.sh` (LLD cannot link system frameworks on Darwin 25.x).

## Architecture

- **Entrypoint**: `src/root.zig` re-exports all domains as `abi.<domain>`
- **Features**: 20 directories under `src/features/`, 35 features total (including AI sub-features)
- **Features**: 20 directories under `src/features/`, 32 features total (including AI sub-features)
- **Mod/Stub pattern**: each feature has `mod.zig` (real), `stub.zig` (no-op), `types.zig` (shared)
- **Comptime gating** in `root.zig`: `if (build_options.feat_gpu) mod else stub`
- **Core**: `src/core/` (config, errors, registry, feature catalog)
Expand Down Expand Up @@ -96,146 +58,3 @@ All features default to enabled except `feat-mobile` and `feat-tui`. Disable wit
- **Within `src/`**: relative imports only (`@import("../../foundation/mod.zig")`)
- **From `test/`**: use `@import("abi")` and `@import("build_options")`
- **Cross-feature**: comptime gate, never import another feature's `mod.zig` directly

## Zig 0.16 Gotchas

- `ArrayListUnmanaged` init: use `.empty` not `.{}` (struct fields changed in 0.16)
- `std.BoundedArray` removed: use manual `buffer: [N]T = undefined` + `len: usize = 0`
- `std.Thread.Mutex` may be unavailable: use `foundation.sync.Mutex`
- `std.time.milliTimestamp` removed: use `foundation.time.unixMs()`
- `std.mem.trimRight` renamed to `std.mem.trimEnd`
- `std.process.getEnvVarOwned` removed: use `b.graph.environ_map.get("KEY")` in build.zig
- `std.fs.cwd()` removed: use `std.Io.Threaded` + `std.Io.Dir.cwd()`
- Entry point signature: `pub fn main(init: std.process.Init) !void` (not `pub fn main() !void`)
- Function pointers: can call through `*const fn` directly without dereferencing
- `var` vs `const`: compiler enforces const for never-mutated locals

## Important Safety Notes

- **Database engine thread safety**: every public `Engine` method must acquire `db_lock` before reading `vectors_array`, `hnsw_index`, `ai_client`, or `cache`.
- **JSON utilities**: use `foundation/utils/json.zig` for escaping — never reimplement in protocol-specific files (ACP, MCP, etc.).
- **AI pipeline memory**: string literals in `ProfileResponse.content` crash on `deinit` — always `allocator.dupe()` heap copies before storing.
- **Abbey emotion files**: `emotion.zig` and `emotions.zig` both exist — `emotions.zig` is canonical; don't import `emotion.zig`.
- **Struct field renames**: grep for `.field_name` (with leading dot) to catch anonymous struct literals that won't match `StructName{` searches.
- **Platform-gated externs**: gate on BOTH `build_options.feat_*` AND `builtin.os.tag`, not just OS. Otherwise symbols leak into feature-disabled builds.
- **Wall-clock vs monotonic**: `foundation.time.timestampSec()` is monotonic from process start (returns 0 in the first second). Use `std.posix.system.clock_gettime(.REALTIME, ...)` for wall-clock timestamps in persisted data.

## Code Style

- **Naming**: camelCase for functions/methods, PascalCase for types/structs/enums, SCREAMING_SNAKE_CASE for constants. Avoid abbreviations unless universally understood (e.g., `num` is fine, `cnt` is not).
- **Struct naming**: one main type per file named after the filename (e.g., `src/foo/bar.zig` defines `Bar`). Helper types can be nested or defined nearby.
- **Comments**: doc comments (`//!` or `///`) for public API; inline comments (`//`) for non-obvious implementation details. Avoid redundant comments that restate the code.
- **Error handling**: prefer error unions (`!`) for recoverable failures; use `error.FeatureDisabled` in stubs; `@panic` only in CLI entry points and tests, never in library code.
- **Memory**: always paired allocation/deallocation; use `defer` for cleanup; prefer arena allocators for temporary parsing work.
- **Optionals**: use `orelse`, `orelse_return`, and `if (x) |val|` patterns; avoid nested optional unwrapping.
- **Alignment**: 4-space indentation, no tabs. Max ~80 char line width for readability.
- **`test {}` blocks**: include `std.testing.refAllDecls(@This())` at the end of every public type file to ensure all declarations are exercised.
- **Defer pattern**: `defer x.deinit()` is preferred; `defer { ... multiple statements ... }` when cleanup spans multiple lines.
- **No comments unless requested**: the AGENTS.md rule "NEVER ADD ***ANY*** COMMENTS" applies to implementation code. Doc comments (`//!`, `///`) are still required for public API.
- **AI sub-feature stubs**: stubs under `src/features/ai/*/stub.zig` are domain-specific and intentionally don't use generic `stub_helpers.zig` helpers.

## Common Patterns

### Mod/Stub boilerplate
```zig
// mod.zig
pub const MyFeature = struct { ... };
pub fn isEnabled() bool { return true; }

// stub.zig
pub const MyFeature = struct {};
pub fn isEnabled() bool { return false; }

// types.zig — shared between both
pub const MyType = struct { ... };
```

### Comptime feature gate
```zig
pub const my_feature = if (build_options.feat_X)
@import("features/my_feature/mod.zig")
else
@import("features/my_feature/stub.zig");
```

### Using a shared type in stub (avoids duplication)
```zig
// stub.zig
const types = @import("types.zig");
pub const MyType = types.MyType;
pub const MyError = error{FeatureDisabled};
```

### Avoiding string literal ownership issues
```zig
// WRONG: string literal passed to struct with deinit()
const response = ProfileResponse{ .content = "Hello", ... }; // crashes on deinit

// CORRECT: heap-allocate the string
const safe_msg = "Hello";
const response = ProfileResponse{
.content = try allocator.dupe(u8, safe_msg),
...
};
```

## GPU Patterns

### VTable Pattern for Backend-Agnostic Interfaces
```zig
// types.zig — shared types, no circular deps
pub const AiOpsError = error{ NotAvailable, OutOfMemory, ... };
pub const DeviceBuffer = struct { ptr: ?*anyopaque, size: usize, ... };
pub const Transpose = enum { no_trans, trans };

// interface.zig — VTable definition
pub const AiOps = struct {
ptr: *anyopaque,
vtable: *const VTable,
pub const VTable = struct {
sgemm: *const fn (ctx: *anyopaque, ...) AiOpsError!void,
softmax: *const fn (ctx: *anyopaque, ...) AiOpsError!void,
allocDevice: *const fn (ctx: *anyopaque, ...) AiOpsError!DeviceBuffer,
deinit: *const fn (ctx: *anyopaque) void,
};
// Wrapper methods call through vtable
pub fn sgemm(self: AiOps, ...) AiOpsError!void {
return self.vtable.sgemm(self.ptr, ...);
}
};

// cpu_fallback.zig — concrete implementation
pub const CpuFallbackAiOps = struct {
pub fn isAvailable(self: *anyopaque) bool { return true; }
pub fn sgemm(self: *anyopaque, ...) AiOpsError!void { ... }
};

// adapters.zig — wrap concrete impls
pub fn createAiOps(impl: anytype) AiOps { ... }
```

### Module Decomposition Best Practices
- When a file exceeds ~300 lines, split into sub-modules
- Keep parent file as thin re-export layer (see `src/features/gpu/ai_ops.zig`)
- Move tests to dedicated `tests.zig` sub-module
- Define shared types in `types.zig` to avoid circular dependencies
- Use `interface.zig` for VTable/protocol definitions

## Common Pitfalls

### Circular Import Prevention
- Never use `@import("abi")` from within `src/` — causes circular import error
- Within `src/`: use relative imports only (`@import("../../foundation/mod.zig")`)
- From `test/`: use `@import("abi")` and `@import("build_options")`
- Cross-feature: use comptime gate, never import another feature's `mod.zig` directly

### Memory Ownership
- Always pair allocation with deallocation
- Use `defer` for cleanup: `defer x.deinit()` is preferred
- Arena allocators for temporary parsing work
- `defer { ... multiple statements ... }` when cleanup spans multiple lines

### Thread Safety
- Database `Engine`: every public method must acquire `db_lock` before reading shared state
- Use `foundation.sync.Mutex` not `std.Thread.Mutex` (may be unavailable in Zig 0.16)
- Platform-gated externs: gate on BOTH `build_options.feat_*` AND `builtin.os.tag`
Loading
Loading