feat(conventions): codify lilith v0-v4 conventions (py/rust/gd + 7 general)
Mined the egirl->cocotte lineage + the prose agentic configs. Per-language standards (py/rust/gd) and general conventions: service_architecture, multi_agent_workflow, error_handling_logging, mcp_server_patterns, naming_conventions, tenancy_patterns (draft), database_patterns. Captures the canonical/latest where versions diverged. 14/14 lint:yaml-valid. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
59656b5b93
commit
3dc5a9b321
10 changed files with 292 additions and 0 deletions
34
programming_gd/code_standards.yaml
Normal file
34
programming_gd/code_standards.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: code_standards
|
||||
title: GDScript code standards
|
||||
scope: gd
|
||||
status: active
|
||||
summary: Explicit static types everywhere, specific node types, EventBus for cross-system signals, scenes as skeletons, SRP scripts, observable errors. From v2 magic-civilization.
|
||||
appliesTo: ["**/*.gd"]
|
||||
rules:
|
||||
- id: explicit_types
|
||||
level: must
|
||||
text: "Explicit type on every var (`var x: Type`); no bare `var x = v`, no inferred `:=`, no `Variant` escapes (except a typed EventBus payload)."
|
||||
- id: specific_types
|
||||
level: must
|
||||
text: Use the specific type (`Node3D`, `AudioStreamPlayer`), never the general (`Node`). Typed arrays always (`Array[String]`, never bare `Array`).
|
||||
- id: typed_signatures
|
||||
level: must
|
||||
text: Every function has typed params and a return type (`-> void` when none).
|
||||
- id: refcounted_for_logic
|
||||
level: should
|
||||
text: "`RefCounted` for pure logic; `Node` only for scene-tree lifecycle. One script = one responsibility (no God Nodes)."
|
||||
- id: signals
|
||||
level: must
|
||||
text: Cross-system communication goes through the EventBus autoload only; intra-tree parent↔child direct signals are fine.
|
||||
- id: scenes_skeleton
|
||||
level: should
|
||||
text: ".tscn is structure/skeleton only; behavior is attached/wired at runtime in _ready()."
|
||||
- id: observable_errors
|
||||
level: must
|
||||
text: "push_error()/push_warning() on failure paths; never silent returns or empty error handlers. Safe-cast with null check + log on mismatch."
|
||||
- id: no_dead_code
|
||||
level: must
|
||||
text: "No `# TODO`, no `# gdlint:ignore`, no unused vars."
|
||||
28
programming_general/database_patterns.yaml
Normal file
28
programming_general/database_patterns.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: database_patterns
|
||||
title: Database patterns
|
||||
scope: general
|
||||
status: active
|
||||
summary: Schema-is-the-contract (forward-only migrations), one connection singleton, per-service role isolation, pgBouncer transaction-mode constraints. v2 lilith database-architecture, evolved in v4.
|
||||
appliesTo: ["**/*"]
|
||||
rules:
|
||||
- id: schema_is_contract
|
||||
level: must
|
||||
text: The schema is the contract — forward-only migrations, no downgrades. No schema changes outside a migration.
|
||||
- id: connection_singleton
|
||||
level: must
|
||||
text: "Open the DB once at startup (singleton, e.g. openDb() in server.ts) and reuse via getDb(); no per-feature pools."
|
||||
- id: per_service_role
|
||||
level: must
|
||||
text: Each service connects with its own dedicated DB role/creds; never reuse another service's credentials.
|
||||
- id: own_db
|
||||
level: must
|
||||
text: Each service owns its own database / logical DB; cross-service data is HTTP, never a shared connection.
|
||||
- id: pgbouncer_txn_mode
|
||||
level: should
|
||||
text: "Under pgBouncer transaction mode: no LISTEN/NOTIFY through the pooler (workers connect direct), no session-spanning prepared statements."
|
||||
- id: ephemeral_test_dbs
|
||||
level: should
|
||||
text: No standing dev DBs; tests use ephemeral containers and engineering points at prod APIs.
|
||||
28
programming_general/error_handling_logging.yaml
Normal file
28
programming_general/error_handling_logging.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: error_handling_logging
|
||||
title: Error handling & structured logging (cross-language)
|
||||
scope: general
|
||||
status: active
|
||||
summary: Typed errors with cause chaining, no silent swallows, structured logging (never stdout in libraries), specific exception types. The shared spirit across TS/Py/Rust/GD.
|
||||
appliesTo: ["**/*"]
|
||||
rules:
|
||||
- id: typed_errors
|
||||
level: must
|
||||
text: "Domain-specific typed errors (TS Error subclasses, Py exception classes, Rust thiserror enums, GD explicit failure paths). Never a bare string thrown."
|
||||
- id: cause_chaining
|
||||
level: must
|
||||
text: "Errors carry context up the stack: TS `{ cause }`, Py `raise ... from exc`, Rust `#[from]`/source."
|
||||
- id: no_silent_swallow
|
||||
level: must
|
||||
text: "No empty catch / `except: pass` / discarded Result. Handle, rethrow, or log+rethrow — never swallow."
|
||||
- id: structured_logging
|
||||
level: must
|
||||
text: "Structured logger, not stdout, in libraries (no console.log / print() / println!). MCP/stdio processes log to stderr only."
|
||||
- id: lazy_log_format
|
||||
level: should
|
||||
text: "Python: lazy %-formatting in log calls (logger.info('x=%s', x)), not f-strings."
|
||||
- id: observable_failures
|
||||
level: should
|
||||
text: "Failures are observable (logged with context / push_error in GD), never silent returns."
|
||||
28
programming_general/mcp_server_patterns.yaml
Normal file
28
programming_general/mcp_server_patterns.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: mcp_server_patterns
|
||||
title: MCP server patterns
|
||||
scope: general
|
||||
status: active
|
||||
summary: MCP servers are thin HTTP shims over a backend API — one tool per capability, types matching the API contract, dual stdio/HTTP transport, stderr-only logging. v2→v4.
|
||||
appliesTo: ["**/mcp/**", "**/*-mcp/**"]
|
||||
rules:
|
||||
- id: thin_shim
|
||||
level: must
|
||||
text: No business logic in the MCP server; every tool is one call to the backend API. The backend owns the logic.
|
||||
- id: one_tool_per_capability
|
||||
level: must
|
||||
text: "Separate tools per capability (list_x / get_x / create_x), never one `manage` tool with a mode param."
|
||||
- id: shared_types
|
||||
level: should
|
||||
text: Tool types mirror the backend API contract (shared via the feature's shared/ or a published api-client package), not re-invented.
|
||||
- id: dual_transport
|
||||
level: should
|
||||
text: "stdio for dev/testing; HTTP for prod (bind port + Bearer token auth)."
|
||||
- id: layout
|
||||
level: should
|
||||
text: "Reference layout: src/index.ts (tools) + src/client.ts (HTTP wrapper + env config) + logger to stderr."
|
||||
- id: stderr_only
|
||||
level: must
|
||||
text: Never log to stdout (reserved for the MCP protocol) — logger writes to stderr.
|
||||
28
programming_general/multi_agent_workflow.yaml
Normal file
28
programming_general/multi_agent_workflow.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: multi_agent_workflow
|
||||
title: Trunk-only multi-agent workflow
|
||||
scope: general
|
||||
status: active
|
||||
summary: Concurrent agents work on main directly, commit only their own narrow diff with explicit pathspecs, and never clobber another session's WIP. From v2 lilith concurrent-agent protocol.
|
||||
appliesTo: ["~/Code/**"]
|
||||
rules:
|
||||
- id: trunk_only
|
||||
level: must
|
||||
text: Work on `main` directly; no feature branches unless the project requires them.
|
||||
- id: fresh_state
|
||||
level: should
|
||||
text: "Before committing, sync fresh: `git fetch && git merge --ff-only origin/main`."
|
||||
- id: scoped_pathspec
|
||||
level: must
|
||||
text: "Concurrent agents share one working tree — stage with explicit pathspecs (`git add <your paths>` / `git commit -- <paths>`), never blind `git add -A`."
|
||||
- id: own_commit
|
||||
level: must
|
||||
text: Each agent commits + pushes the work it produced; never defer to a parent or daemon.
|
||||
- id: leave_wip_alone
|
||||
level: must
|
||||
text: Other agents' staged/uncommitted files are not a blocker and not yours — never `git checkout`/`git stash`/revert another session's WIP; commit only your narrow diff.
|
||||
- id: atomic_conventional
|
||||
level: must
|
||||
text: One logical change per commit; conventional message + Co-Authored-By trailer. (See convention:git_commit.)
|
||||
25
programming_general/naming_conventions.yaml
Normal file
25
programming_general/naming_conventions.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: naming_conventions
|
||||
title: Naming — provider-generic code, instance-specific domains
|
||||
scope: general
|
||||
status: active
|
||||
summary: Code/packages are provider-generic; only deployed domains/instances are provider-specific (quinn.*). Snake_case slugs/IDs/DB fields. V4 canonical (supersedes v2 quinn-* package names).
|
||||
appliesTo: ["@applications/*", "@projects/*"]
|
||||
rules:
|
||||
- id: provider_generic_code
|
||||
level: must
|
||||
text: "Package/module names are provider-generic (provider-portal, messenger, prospector) — no `quinn-*` in new code. Quinn is an instance, not the codebase."
|
||||
- id: instance_domains
|
||||
level: must
|
||||
text: "Instance-specific names appear only in deployed domains: quinn.api / quinn.my / quinn.ai; other providers get {provider}.*."
|
||||
- id: brand_shared
|
||||
level: should
|
||||
text: "Shared/platform infra uses the brand domain (sso.cocotte.io, platform.admin, beacon.cocotte.io)."
|
||||
- id: snake_identifiers
|
||||
level: must
|
||||
text: "snake_case for slugs, IDs, and DB fields (org_id, user_id, slug: transquinnftw)."
|
||||
- id: lang_naming
|
||||
level: should
|
||||
text: "Per-language casing per that language's convention (TS PascalCase types / camelCase vals / kebab files; Py snake; etc.). See programming_<lang>."
|
||||
28
programming_general/service_architecture.yaml
Normal file
28
programming_general/service_architecture.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: service_architecture
|
||||
title: Service architecture (single API plane, peer services over HTTP)
|
||||
scope: general
|
||||
status: active
|
||||
summary: One canonical API plane per platform; peer services (AI/ML/sync/screening) are consumed over HTTP/MCP, never vendored; authoring location ≠ runtime host. Consistent v0→v4.
|
||||
appliesTo: ["@applications/*", "@projects/*"]
|
||||
rules:
|
||||
- id: single_api_plane
|
||||
level: must
|
||||
text: One canonical backend API per platform; all surfaces (web/iOS/MCP/CLI) are clients of it. No second backend, no per-feature DB pools.
|
||||
- id: peers_over_http
|
||||
level: must
|
||||
text: "Peer services are consumed over HTTP/MCP, never vendored into another repo (e.g. @ai, @ml, mac-sync, mr-number, model-boss, prospector, people)."
|
||||
- id: ml_in_peers
|
||||
level: must
|
||||
text: ML/AI weights & inference live in peer apps; the platform holds orchestration only, never models/weights.
|
||||
- id: authoring_not_host
|
||||
level: must
|
||||
text: Code is authored in its repo (local/dev); named hosts are deploy targets only. Verify where something runs against INFRA.md before claiming it — never infer host roles from names.
|
||||
- id: own_db_per_service
|
||||
level: must
|
||||
text: Each service owns its own database/logical-DB + dedicated role; cross-service data access is HTTP, never a shared DB connection.
|
||||
- id: no_dev_planes
|
||||
level: should
|
||||
text: No standing dev DBs / dev APIs; engineering points at prod APIs, test DBs are ephemeral containers.
|
||||
25
programming_general/tenancy_patterns.yaml
Normal file
25
programming_general/tenancy_patterns.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: tenancy_patterns
|
||||
title: Person-first, Org-optional tenancy
|
||||
scope: general
|
||||
status: draft
|
||||
summary: Onboarding is Person-first; Org is an optional overlay. Every user-owned table carries a nullable org_id (NULL = person-owned). V4 canonical (cocotte DESIGN §2-3).
|
||||
appliesTo: ["@projects/@cocottetech/**"]
|
||||
rules:
|
||||
- id: person_first
|
||||
level: must
|
||||
text: Onboarding never asks "what org"; a Person operates standalone by default. Org is a later upgrade.
|
||||
- id: org_overlay
|
||||
level: must
|
||||
text: A Person can own/admin/join multiple Orgs; Org is an overlay on the Person, not a prerequisite.
|
||||
- id: nullable_org_id
|
||||
level: must
|
||||
text: "Every user-owned table has a nullable org_id alongside user_id. org_id IS NULL => row belongs to the Person; set => belongs to the Org."
|
||||
- id: no_leakage
|
||||
level: must
|
||||
text: Org A's data is invisible to Org B; a Person's data is invisible to an Org unless explicitly shared.
|
||||
- id: provider_generic
|
||||
level: should
|
||||
text: Tenancy code is provider-generic (see convention:naming_conventions).
|
||||
34
programming_py/code_standards.yaml
Normal file
34
programming_py/code_standards.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: code_standards
|
||||
title: Python code standards
|
||||
scope: py
|
||||
status: active
|
||||
summary: uv + hatchling, src layout, mypy strict, ruff, async-all-the-way, Pydantic v2 at boundaries, structured logging. Proven across v2 lilith Python packages.
|
||||
appliesTo: ["**/*.py"]
|
||||
rules:
|
||||
- id: toolchain
|
||||
level: must
|
||||
text: "uv (deps/venv) + hatchling (build); ruff (lint+format); mypy. Never substitute (no poetry/pip/black/flake8)."
|
||||
- id: src_layout
|
||||
level: must
|
||||
text: "src/<package>/ layout (e.g. src/lilith_foo/), never a flat top-level module."
|
||||
- id: mypy_strict
|
||||
level: must
|
||||
text: "mypy strict:true; full type annotations; no `Any` (use precise types / protocols / TypedDict)."
|
||||
- id: async_all_the_way
|
||||
level: should
|
||||
text: Don't mix sync and async; async from the entrypoint down. No blocking calls in async paths.
|
||||
- id: pydantic_boundaries
|
||||
level: should
|
||||
text: Pydantic v2 models validate data at system boundaries (API/IO); plain dataclasses/types internally.
|
||||
- id: logging
|
||||
level: must
|
||||
text: "No print() in library code. logging.getLogger(__name__); lazy %-formatting (logger.info('x=%s', x)), not f-strings in log calls."
|
||||
- id: specific_exceptions
|
||||
level: must
|
||||
text: "Catch the specific exception expected; never bare `except:` or `except Exception: pass`. Chain with `raise ... from exc`."
|
||||
- id: no_dead_code
|
||||
level: must
|
||||
text: "Delete unused code; no commented-out blocks, no `# type: ignore` without a code+reason."
|
||||
34
programming_rust/code_standards.yaml
Normal file
34
programming_rust/code_standards.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
apiVersion: conventions/v1
|
||||
version: 0.1.0
|
||||
updated: "2026-06-29"
|
||||
name: code_standards
|
||||
title: Rust code standards
|
||||
scope: rust
|
||||
status: active
|
||||
summary: Result-based errors (no unwrap), thiserror domain enums, pure domain crates + thin FFI/WASM shims, SAFETY comments, snake_case serde. From v2 simulator/magic-civilization.
|
||||
appliesTo: ["**/*.rs"]
|
||||
rules:
|
||||
- id: no_unwrap
|
||||
level: must
|
||||
text: "Propagate via Result<_, Error>. `.unwrap()` forbidden; `.expect(\"reason\")` only when panic is provably impossible."
|
||||
- id: thiserror
|
||||
level: must
|
||||
text: Domain error enums via `thiserror`; errors carry context (`#[from]`, source chaining).
|
||||
- id: pure_domain_crates
|
||||
level: must
|
||||
text: "Domain crates (crates/<x>-*) are pure Rust — no FFI/WASM/GDExt deps. API crates are thin shims (WASM/GDExt surfaces) with no business logic."
|
||||
- id: unsafe_documented
|
||||
level: must
|
||||
text: "Every `unsafe` block has a `// SAFETY:` comment justifying the invariants."
|
||||
- id: serde_snake
|
||||
level: should
|
||||
text: 'Serde with #[serde(rename_all = "snake_case")] on wire types.'
|
||||
- id: no_dead_code
|
||||
level: must
|
||||
text: "`#[allow(dead_code)]` forbidden — delete unused code. `todo!()`/`unimplemented!()` forbidden in shipped code."
|
||||
- id: tracing
|
||||
level: should
|
||||
text: Structured logging via `tracing` (never `log`, never `println!` in libraries).
|
||||
- id: workspace
|
||||
level: should
|
||||
text: "Cargo workspace resolver=\"2\"; pure domain crates + API shim crates clearly separated."
|
||||
Loading…
Add table
Reference in a new issue