feat(conventions): seed central workspace+coding conventions repo

convention.yaml.schema (meta-schema) + per-language dirs (general/ts/swift/py/
rust/gd). Seed conventions: recursive_code_workspace (the ~/Code @org tree,
always-active), infra_manifest (per-project .infra.yaml + its schema), and
ts/code_standards + general/git_commit (shifted from the prose agentic configs).
Referenced by global config as convention:<name>(<args>).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-29 08:19:39 -04:00
commit 48d4853685
10 changed files with 253 additions and 0 deletions

36
README.md Normal file
View file

@ -0,0 +1,36 @@
# @conventions
Single source of truth for workspace + coding conventions, shared by every project
under `~/Code` (and known to global agentic config, so any agent in any repo can
resolve them).
## Layout
```
@conventions/
├── convention.yaml.schema # meta-schema every convention validates against
├── programming_general/<name>.yaml # language-agnostic conventions
├── programming_ts/<name>.yaml # TypeScript
├── programming_swift/<name>.yaml # Swift
├── programming_py/<name>.yaml # Python
├── programming_rust/<name>.yaml # Rust
└── programming_gd/<name>.yaml # GDScript
```
## A convention
A `<name>.yaml` is metadata + `rules`, and may:
- **accept `params`** — referenced as `convention:<name>(<args>)`, e.g. `convention:recursive_code_workspace(~/Code)`;
- **define a manifest** via `providesFile` — a file (e.g. `.infra.yaml`) every conforming project must contain, with a nested JSON Schema for its contents.
Validate a convention file against `convention.yaml.schema`; validate a project's
manifest against the convention's `providesFile.schema`.
## How agents use it
Global config points agents here. An agent resolves `convention:<name>` by reading
`programming_<scope>/<name>.yaml`, applies its `rules`, and (for `providesFile`
conventions) reads/writes the project's manifest to that schema. Conventions are
data, not prose — tooling (a future `infra-apply`, linters, scaffolders) can consume
them directly.
## Seed conventions
- `programming_general/recursive_code_workspace.yaml` — the `~/Code` @org layout.
- `programming_general/infra-manifest.yaml` — per-project `.infra.yaml`.

69
convention.yaml.schema Normal file
View file

@ -0,0 +1,69 @@
# JSON Schema (draft 2020-12, written in YAML) for a single workspace convention.
# Every file at @conventions/programming_<scope>/<name>.yaml validates against this.
# Agents reference a convention as convention:<name> or convention:<name>(<args>).
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "conventions/convention.yaml.schema"
title: Convention
description: One workspace convention — metadata + rules, optionally defining a project manifest file and/or accepting parameters.
type: object
additionalProperties: false
required: [name, title, scope, status, summary]
properties:
name:
type: string
pattern: "^[a-z0-9][a-z0-9_]*$"
description: snake_case id; MUST match the filename (without .yaml).
title:
type: string
scope:
type: string
enum: [general, ts, swift, py, rust, gd]
description: Matches the programming_<scope>/ directory the file lives in.
status:
type: string
enum: [draft, active, deprecated]
summary:
type: string
description: One line; shown when an agent lists available conventions.
params:
type: array
description: Named arguments the convention accepts, e.g. recursive_code_workspace(root).
items:
type: object
additionalProperties: false
required: [name]
properties:
name: { type: string }
description: { type: string }
default: {}
appliesTo:
type: array
description: Globs or project kinds this convention governs.
items: { type: string }
supersedes:
type: array
items: { type: string }
rules:
type: array
items:
type: object
additionalProperties: false
required: [id, level, text]
properties:
id: { type: string }
level: { type: string, enum: [must, should, may] }
text: { type: string }
rationale: { type: string }
# When set, the convention defines a manifest file every conforming project must
# provide (e.g. .infra.yaml), with a nested JSON Schema for that file's contents.
providesFile:
type: object
additionalProperties: false
required: [path, schema]
properties:
path:
type: string
description: Repo-relative path each conforming project must contain (e.g. ".infra.yaml").
schema:
type: object
description: JSON Schema (draft 2020-12) the manifest file validates against.

0
programming_gd/.gitkeep Normal file
View file

View file

@ -0,0 +1,25 @@
name: git_commit
title: Atomic commit + push protocol
scope: general
status: active
summary: Each verified logical change is its own scoped, conventional commit, pushed immediately; every agent commits the work it produced.
appliesTo: ["~/Code/**"]
rules:
- id: atomic
level: must
text: One logical, verified change per commit. Stage with scoped `git add <paths>` — never blind `git add -A`.
- id: verify_first
level: must
text: Tests / build / typecheck pass before the commit. Never commit unverified code.
- id: conventional
level: must
text: Conventional-commit message; end with the Co-Authored-By trailer.
- id: push_after
level: must
text: git push (fast-forward only) after each commit so work isn't stranded on one host. Never force-push.
- id: agent_owns_commit
level: must
text: Each agent (orchestrator or sub) commits + pushes its own work; never defer to a parent or a daemon. (The apricot ACS auto-commit-service is stale/offline.)
- id: branch
level: should
text: These repos work on main directly; don't auto-create branches unless the project requires it.

View file

@ -0,0 +1,54 @@
name: infra_manifest
title: Per-project infra manifest (.infra.yaml)
scope: general
status: draft
summary: Every deployable project declares its infrastructure in a root .infra.yaml; a future infra-apply tool renders it to the provider (DO / Terraform).
appliesTo: ["@applications/*", "@projects/@cocottetech", "@projects/@magic-civilization"]
rules:
- id: own_db
level: must
text: A project needing a database declares its own logical DB + dedicated user on the shared managed cluster (data-sourced), never reusing another service's creds.
rationale: own-DB-per-service + credential separation.
- id: http_coupling
level: must
text: Cross-service dependencies are HTTP only (declared in depends_on), never shared databases.
- id: gpu_ondemand
level: should
text: GPU workloads are on-demand — provision, keep warm while the queue is deep, release on idle. Never a standing GPU.
providesFile:
path: .infra.yaml
schema:
$schema: "https://json-schema.org/draft/2020-12/schema"
title: ProjectInfraManifest
type: object
additionalProperties: false
required: [project, provider]
properties:
project: { type: string }
provider: { type: string, enum: [digitalocean] }
database:
type: object
additionalProperties: false
required: [cluster, name, user]
properties:
cluster: { type: string, description: Shared managed cluster — data-sourced, not owned here. }
name: { type: string }
user: { type: string }
service:
type: object
additionalProperties: false
properties:
host: { type: string }
runtime: { type: string }
port: { type: integer }
systemd_unit: { type: string }
gpu:
type: object
additionalProperties: false
properties:
mode: { type: string, enum: [on-demand] }
droplet: { type: string }
depends_on:
type: array
items: { type: string }
description: Other services consumed over HTTP.

View file

@ -0,0 +1,29 @@
name: recursive_code_workspace
title: Recursive code workspace (~/Code @org tree)
scope: general
status: active
summary: ~/Code is a recursive tree of @org dirs, each holding many independent git repos; tools and agents resolve projects by walking it.
params:
- name: root
description: Workspace root to walk.
default: "~/Code"
appliesTo: ["~/Code/**"]
rules:
- id: orgs
level: must
text: >
Top-level @org dirs partition the workspace: @applications (apps/services you
operate), @projects (platforms incl. @cocottetech, @magic-civilization, @lilith),
@packages (shared libraries), @conventions (this repo), plus support buckets
@scripts / @docs / @forks / @external / @archives / @work.
- id: independent_repos
level: must
text: Each project under an @org is its own independent git repo. Never assume a single monorepo at the root.
rationale: Repos are cloned/forge-managed per-project; cross-repo coupling is HTTP/registry, not filesystem.
- id: per_project_infra
level: should
text: A deployable project owns its infra via a root .infra.yaml (see convention:infra-manifest). Shared infra (e.g. the managed PG cluster) is data-sourced, not owned per-project.
rationale: Catch-all shared infra repos (uvlava-style) are superseded by per-project ownership.
- id: support_buckets
level: may
text: Cross-cutting or transient items live in the support buckets, not inside project repos.

0
programming_py/.gitkeep Normal file
View file

View file

View file

View file

@ -0,0 +1,40 @@
name: code_standards
title: TypeScript code standards
scope: ts
status: active
summary: Portable TS conventions — strict typing, ESM, typed errors, named exports, Vitest. Project-specific architecture (kernels, golden-vectors, file-size caps) lives with the project, not here.
appliesTo: ["**/*.ts", "**/*.tsx"]
rules:
- id: strict
level: must
text: "tsconfig strict:true; prefer noUncheckedIndexedAccess + exactOptionalPropertyTypes. No escape hatches."
- id: no_any
level: must
text: No `any`; use `unknown` at boundaries and narrow with type guards. Never `as unknown as T`.
- id: no_ts_ignore
level: must
text: No `@ts-ignore`; fix the type, or `@ts-expect-error` with a reason.
- id: explicit_return_types
level: should
text: Explicit return types on exported functions and React components.
- id: discriminated_unions
level: should
text: Discriminated unions over boolean flags; add variants via switch, don't branch on strings.
- id: typed_errors
level: must
text: Typed Error subclasses with cause chaining. Never empty catch; never catch-and-only-console.
- id: esm_named_exports
level: must
text: ESM only; named exports in libraries (default exports only in app pages/components); index.ts is the public manifest.
- id: no_link_file
level: must
text: Monorepo deps via workspace:* or the registry — never file:/link:.
- id: vitest
level: must
text: Vitest (not Jest); *.test.ts co-located, full-sentence it(...); mocks only in test files.
- id: structured_logging
level: must
text: No console.log in library code — use a structured logger.
- id: no_dead_code
level: must
text: Delete unused code entirely; no _unused prefixes, no commented-out blocks.