commit 48d4853685e1382bacfff96ef43a11453f2154b6 Author: Natalie Date: Mon Jun 29 08:19:39 2026 -0400 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:(). Co-Authored-By: Claude Opus 4.8 (1M context) diff --git a/README.md b/README.md new file mode 100644 index 0000000..6c05c0a --- /dev/null +++ b/README.md @@ -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/.yaml # language-agnostic conventions +├── programming_ts/.yaml # TypeScript +├── programming_swift/.yaml # Swift +├── programming_py/.yaml # Python +├── programming_rust/.yaml # Rust +└── programming_gd/.yaml # GDScript +``` + +## A convention +A `.yaml` is metadata + `rules`, and may: +- **accept `params`** — referenced as `convention:()`, 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:` by reading +`programming_/.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`. diff --git a/convention.yaml.schema b/convention.yaml.schema new file mode 100644 index 0000000..55a37c1 --- /dev/null +++ b/convention.yaml.schema @@ -0,0 +1,69 @@ +# JSON Schema (draft 2020-12, written in YAML) for a single workspace convention. +# Every file at @conventions/programming_/.yaml validates against this. +# Agents reference a convention as convention: or convention:(). +$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_/ 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. diff --git a/programming_gd/.gitkeep b/programming_gd/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/programming_general/git_commit.yaml b/programming_general/git_commit.yaml new file mode 100644 index 0000000..4fdab0e --- /dev/null +++ b/programming_general/git_commit.yaml @@ -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 ` — 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. diff --git a/programming_general/infra-manifest.yaml b/programming_general/infra-manifest.yaml new file mode 100644 index 0000000..3cfd594 --- /dev/null +++ b/programming_general/infra-manifest.yaml @@ -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. diff --git a/programming_general/recursive_code_workspace.yaml b/programming_general/recursive_code_workspace.yaml new file mode 100644 index 0000000..93c5966 --- /dev/null +++ b/programming_general/recursive_code_workspace.yaml @@ -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. diff --git a/programming_py/.gitkeep b/programming_py/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/programming_rust/.gitkeep b/programming_rust/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/programming_swift/.gitkeep b/programming_swift/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/programming_ts/code_standards.yaml b/programming_ts/code_standards.yaml new file mode 100644 index 0000000..fa28c8c --- /dev/null +++ b/programming_ts/code_standards.yaml @@ -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.