Enforcement
There is no linter, no formatter and no type-checker anywhere in the
estate. Four tests/ci/ structural guards are the entire automated enforcement
surface, and one of them has never worked. This page is the day's work that converts a
description into a standard, shipped as files rather than as prose.
A rule without a working test is a suggestion, and you cannot tell the difference from the outside.
The configs
Every file below is real, parses, and is released under CC BY 4.0 with the rest of this site. Copy them; they have no dependencies beyond the tool each one configures.
Three import bans — pydantic, typing.Literal,
boto3 — plus the pyflakes category that runtime type safety does not cover.
Every stylistic rule that would reflow the alignment is explicitly off.
4-space indent, single quotes, no semicolons, trailing commas — and the two
no-restricted-syntax selectors that catch real bugs: a
connectedCallback override, and a new listener on the legacy
sp-cli: namespace.
No literal colours outside the token file — the highest-value CSS rule
available — as color-no-hex, color-named and a
property-value allow-list, with the token file itself carved out.
The seven rules no linter can express, as one TestCase in the style of
the four guards that already exist: banner, no docstrings, no _ methods, one
class per file named for the file, empty __init__.py, no raw primitive
attributes, aligned imports.
The highest-value missing check in the estate: render every Section__* and
pipe the output through shellcheck, plus banner, breadcrumb and unescaped-brace
checks. Skips cleanly when shellcheck is absent, per testing rule T3.
The two JavaScript rules ESLint cannot express: every component directory holds the full
.js/.html/.css triplet, and every
SgComponent subclass declares static jsUrl = import.meta.url.
The two HTML rules that catch defects rather than formatting — every interactive
control has an accessible name, and behaviour never lives in an on*
attribute — plus the fragment rule and the 2-space indent. Standard library only; it
tolerates the unclosed markup an excerpt leaves behind.
What the tools genuinely cannot do
The survey document behind this site lists “no docstrings — trivial, ruff
D-rules inverted” and “a ruff config block” covering four rules. That
is optimistic, and getting it right matters more here than being brief.
| Rule | Can a linter express it? | Where it ends up |
|---|---|---|
Ban pydantic, Literal, boto3 | Yes — ruff flake8-tidy-imports banned-api | ruff.toml |
| No docstrings | No. pydocstyle (D) requires docstrings; there is no inverse rule in ruff or flake8 | a custom AST check |
No _-prefixed methods | No. pep8-naming has no inverse of its private-name rules | custom AST check |
__init__.py empty | No. A file-emptiness check is not a lint rule | custom check |
| Banner present and well-formed | No. A file-header shape check; no rule exists | custom check |
| Filename equals class name | No. A cross-file property; ruff sees one file at a time | custom check |
| No raw primitives as attributes | No. Needs an AST walk over annotated class attributes | custom AST check |
| Import alignment | No, and no formatter will ever produce it. black and gofmt refuse alignment by design | custom check |
| CSS indent and value alignment | No longer. stylelint deprecated every stylistic rule in v15 and removed them in v16, on the grounds that a formatter should own them — and no formatter produces this alignment | custom check |
| Every interactive control has an accessible name | Partly. htmlhint and eslint-plugin-jsx-a11y cover some of this; neither reads a detached component fragment, which is what this markup is | a custom check |
static jsUrl = import.meta.url present | No. ESLint core has no “this class must declare X” rule | a custom check |
The pattern is not an accident. Almost every rule in this house style is a
structural property — of a file, a filename, a directory, a column — and linters are
built to check syntactic properties of one file's contents. That is why the estate's
existing enforcement surface is a folder of TestCase classes rather than a
config, and why the honest recommendation is to keep it that way and add a small linter
alongside for the things linters are good at: undefined names, unused imports, unreachable
code — the category Type_Safe genuinely does not cover.
Running it against the estate's own examples
The guard was run against the two Python files this site quotes verbatim. It is not a demonstration written to pass:
# Safe_Str__IP__Address.py — the constrained-primitive example Ran 7 tests — OK # Schema__Caller__IP.py — the eleven-line "everything in one file" example FAILED (failures=1) test_import_keywords_are_aligned: Schema__Caller__IP.py: imports at 2 different columns [76, 77]
The canonical example of the house style fails the house style's own alignment rule, by one column. It may be a transcription artifact in the survey document rather than a miss in the real file — and that is the whole argument on this page. Today nobody can tell you which, because nothing checks. With the guard in place the question does not come up: either the file is aligned or the build is red.
The markup checker was run the same way, against the component fragment this site quotes
on the HTML page. It reports nothing — and on a
fragment with an onclick, a nameless <button>, a
<template> wrapper and a 3-space indent it reports all four. A check that
has only ever been seen to pass is the failure mode this whole page is about, so both
directions are worth running once.
And one discrepancy the guard surfaced
Rule 7 is documented as a banner with three content lines. Every verbatim example in
the survey has a different number:
Schema__Caller__IP has two — an identity
line and a purpose line — and
Section__Shutdown has four. A guard
written to the documented rule fails every file in the estate, at a measured 100%
compliance.
So the shipped check enforces the shape the code actually has: an identity line that names the file, then at least one purpose line. Writing the guard is what found the discrepancy, which is the second argument for writing guards.
The order to do this in
| # | Fix | Effort | Catches a real defect today? |
|---|---|---|---|
| F1 | Delete [^_] from test_no_legacy_imports.py. The guard has never matched anything; there are 228 real imports across 69 files | 4 characters | Yes — 69 files |
| F4 | shellcheck on rendered Section__* output. The generated shell is currently unlintable because it is never rendered outside production | a test — shipped above | Yes |
| F2 | The ruff config — three import bans plus pyflakes | one block — shipped | Guards against regression |
| F6 | stylelint: no literal colours outside the token file | an hour — shipped | Guards against regression |
| F7 | The banner check | ~20 lines — shipped | Found the rule-7 discrepancy above |
| F8 | Filename equals class name, one class per file | ~20 lines — shipped | 90% today, so yes |
| F3 | Import alignment — the only formatting rule that is measurably inconsistent, at 39% | a check — shipped | Yes — 817 files in scope |
| F5 | eslint, plus the component-triplet check | half a day — shipped | Guards against regression |
Turn any of these on and the build goes red. Rule 9 has 97 violations, rule 21 is at 90%, import alignment is at 39%, and the semicolon rule contradicts half the JavaScript. That is not an argument against turning them on — it is the measurement of what “high compliance, no enforcement” actually costs, and it is only visible once something counts. Start with F1 and F4, which catch defects rather than formatting.
How to grow a guard set
The four working guards in the estate have one thing in common that is worth copying:
each encodes a rule that was violated at least once. The : object = None ban,
the UI-in-wheel check, the two component snapshots. None of them came from a checklist; all of
them came from an incident.
That is also the strongest argument for the two AWS naming rules being written down at all. They cite their own precedent, so a reader who disagrees is arguing with a rejected API call rather than with a preference. Rules 14 and 15 →