89 lines
6.1 KiB
Markdown
89 lines
6.1 KiB
Markdown
|
|
# standards and conventions
|
|||
|
|
|
|||
|
|
_Status: direction (Option B decided) — as of 2026-07-17_
|
|||
|
|
_Connects to: [deterministic-gates](deterministic-gates.md), [agent-design-principles](agent-design-principles.md), [never-ask-twice](never-ask-twice.md), [self-improvement-loops](self-improvement-loops.md), [overview](../OVERVIEW.md)_
|
|||
|
|
|
|||
|
|
## 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](never-ask-twice.md)); 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](deterministic-gates.md)).
|
|||
|
|
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](pipeline-stages.md)).
|
|||
|
|
- **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](pipeline-stages.md)). 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](never-ask-twice.md)
|
|||
|
|
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](self-improvement-loops.md). 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](never-ask-twice.md) (vault-as-source-of-truth split)
|
|||
|
|
- 2026-07-17 design session (this doc's origin)
|