cc-os/plugins/os-sdlc/reference/standards-and-conventions.md

6.1 KiB
Raw Blame History

standards and conventions

Status: direction (Option B decided) — as of 2026-07-17 Connects to: deterministic-gates, agent-design-principles, never-ask-twice, self-improvement-loops, overview

Purpose

Defines once, and reuses everywhere, the coding standards and conventions the factory applies across many languages and frameworks (e.g. Sandi Metz 99-Bottles OOP for all OOP languages) — so a standard is authored a single time, not redefined per project. Load this before designing project onboarding, the green command, or any standard-carrying context packet for a pipeline agent.

Design

  • Three layers, separated on purpose:
    1. The opinion — language-agnostic prose (the Metz rules themselves). Written once.
    2. Per-language bindings — executable configs (.rubocop.yml, eslint fragment, ruff.toml) each tagged with the standard + version it implements. One opinion, N bindings: this is where cross-language overlap resolves.
    3. Per-project adoption — a project copies the right bindings, wires the green command, and records a repo ADR ("adopts sandi-metz-oop v2 for Ruby") linking the vault opinion.
  • Layout — Option B (DECIDED). Opinion lives in the vault as a convention/ note (the cross-project source of truth, per never-ask-twice); executable bindings ship in the plugin because they are deployable, git-versioned/diffable artifacts and the vault is a knowledge graph, not an artifact store. Precedent: plugins/cc-architect/references/conventions/cc-os-naming.md (canonical repo copy; vault note defers to it).
    ~/Documents/SecondBrain/convention/sandi-metz-oop.md   # opinion; links to bindings
    plugins/os-sdlc/standards/sandi-metz-oop/
      standard.md            # canonical prose (vault note defers here)
      ruby/rubocop.yml   javascript/eslintrc.json   python/ruff.toml
    
  • The residue rule: anything lint-enforceable is in nobody's prompt. An agent is never told the arity/length/naming limit — the green command's linter tells it, only on violation, injected into its own next turn (mechanism: deterministic-gates). Standing context cost of enforceable rules is therefore zero. Only the judgment residue a linter cannot check (abstraction choice, "composition over inheritance", pattern selection) is ever routed into a prompt — and only into the reviewer's (pipeline-stages).
  • Routing key: standard × language × audience × facet. Each standard's prose is sectioned by facet (interface / implementation / test) and audience-tagged, so the context packet is a mechanical section-filter, not a per-session judgment call:
    • programmer — gets nothing standing; learns quality rules only as gate feedback.
    • test-writer — is the interface designer in a red-first pipeline, so it gets test conventions plus the interface facet (naming, public message shape, arity-as-interface) — but not implementation-facing rules (method length, private structure, duplication).
    • reviewer — gets the judgment-facet residue; it is the designated judgment actor.
  • How far linting reaches (Ruby, the deep case): rubocop (metrics/style/naming, custom cops for house rules), reek (semantic smells — feature envy, data clumps, control coupling; much of "Metz taste" is reek findings), flay/flog (structural duplication / complexity), mutant (mutation testing — deterministically proves the tests constrain behavior, keeping the test-writer honest and mechanically answering the "gaming the green-assert gate" concern in pipeline-stages). JS (eslint+plugins) / Python (ruff) are shallower but workable. Ceiling: tools detect symptoms, not choices — they police the boundaries of good code; the reviewer polices the choices within them.
  • Pre-seeding, not just mining. The Mobbin move from never-ask-twice generalizes: our own standards are self-sourced pre-seeds — writing convention/sandi-metz-oop up front lets the "OOP approach" decision category start at afk-ready instead of being re-asked per project. This is the concrete cure for "defined Metz a dozen times."
  • Sequencing (hard constraint): all of this is post-tracer-bullet. v1 nails the flow with disposable code and trivial prompts; standards/gate-tuning land afterward, alongside self-improvement-loops. Do not add quality gates before the happy path is trusted.

Open questions

  • Adoption/update ownership. First-time adoption (copy bindings, wire green command) is naturally an os-sdlc onboarding skill. Drift-checking (a project on an outdated standard version) might extend os-status — but os-status:fix is scoped to "the cc-os approach," whereas this layer targets arbitrary client projects in many languages. Ownership unsettled.
  • Gate strictness is a tuning parameter, not an ideology. A maximal cop wall can thrash a cheap model (fix one violation → trigger another) and burn the iteration budget. Tune strictness against iteration counts via autoresearch, same as prompt text — baseline at zero standards first (audit defaults before customizing) and admit each rule only if it beats that baseline.
  • Auditing existing projects is deferred. One cheap harvest now: seed the canonical Metz standard + first Ruby binding from the best of the dozen existing definitions. The other projects reconcile later via the version-check path, one at a time (like ADR rollout).
  • Bindings for non-OOP standards (functional, framework-conformance) — schema unproven.

Sources

  • SecondBrain vault: convention/ facet, matt-pocock-skills-v1-1-changes.md
  • plugins/cc-architect/references/conventions/cc-os-naming.md (Option B precedent)
  • Ruby toolchain: rubocop, reek, flay, flog, mutant
  • ADR-0037; never-ask-twice (vault-as-source-of-truth split)
  • 2026-07-17 design session (this doc's origin)