{
  "schema": "coding-site-rules/v1",
  "source": {
    "file": ".claude/CLAUDE.md",
    "repo": "SGraph-AI__Service__Playwright",
    "note": "An agent instruction file, not a published standard. 31 numbered rules plus 4 non-negotiable testing rules.",
    "measured_by": "briefs/06__the-rules-and-compliance.md",
    "surveyed": "2026-08-24"
  },
  "badges": {
    "tested": {
      "glyph": "✅",
      "label": "enforced by a test",
      "meaning": "a CI guard fails the build when the rule is broken"
    },
    "claimed": {
      "glyph": "❓",
      "label": "guard claimed, unlisted",
      "meaning": "the source says a CI guard enforces this, but the guard is not in the source's own table of guards"
    },
    "documented": {
      "glyph": "🟡",
      "label": "documented, unenforced",
      "meaning": "written down, high measured compliance, nothing checks it"
    },
    "violated": {
      "glyph": "⚠️",
      "label": "documented, violated",
      "meaning": "written down, unenforced, and measurably broken in the tree"
    },
    "unwritten": {
      "glyph": "❌",
      "label": "undocumented convention",
      "meaning": "consistent in the code, and no rule anywhere says so"
    }
  },
  "groups": [
    {
      "id": "code-patterns",
      "title": "Code patterns",
      "range": "1–9",
      "blurb": "The nine that define the Python style. Every one of them is measurable, and every one of them was measured.",
      "rules": [
        {
          "n": 1,
          "text": "All classes extend <code>Type_Safe</code> — no plain Python classes.",
          "verbatim": false,
          "measured": "506 direct, plus <code>Type_Safe__List</code> (44), <code>Fast_API__Routes</code> (46) and <code>Schema__Step__Base</code> (25), which are themselves descendants. Excluding 169 <code>TestCase</code> and 77 enums, essentially every class is in the lineage.",
          "badge": "documented",
          "note": "Do not publish “48% extend Type_Safe”. It is true and it is misleading: the naive percentage counts test cases and enums as failures."
        },
        {
          "n": 2,
          "text": "Zero raw primitives — no <code>str</code>, <code>int</code>, <code>float</code>, <code>list</code> or <code>dict</code> as attributes.",
          "verbatim": true,
          "measured": "27 hand-written constrained primitives in <code>sg_compute/primitives/</code> alone; 78 <code>Safe_Str</code>, 13 <code>Safe_Int</code> and 6 <code>Safe_UInt</code> subclasses in the new tree.",
          "badge": "documented",
          "note": "The highest-value rule in the set and the hardest to express as a lint. It would need a custom AST check."
        },
        {
          "n": 3,
          "text": "No <code>Literal</code>s — fixed value sets use <code>Enum__*</code>.",
          "verbatim": false,
          "measured": "185 <code>Enum__*</code> files.",
          "badge": "documented",
          "note": "Pairs with the tooling rule <i>“Never use Pydantic. No Literals.”</i>"
        },
        {
          "n": 4,
          "text": "Schemas are pure data — no methods.",
          "verbatim": false,
          "measured": "614 <code>Schema__*</code> files, spot-checked.",
          "badge": "documented"
        },
        {
          "n": 5,
          "text": "Collection subclasses are pure type definitions.",
          "verbatim": false,
          "measured": "44 <code>Type_Safe__List</code> subclasses.",
          "badge": "documented"
        },
        {
          "n": 6,
          "text": "Every route returns <code>.json()</code> on a <code>Type_Safe</code> schema — no raw dicts.",
          "verbatim": false,
          "measured": "46 <code>Fast_API__Routes</code> subclasses, spot-checked.",
          "badge": "documented"
        },
        {
          "n": 7,
          "text": "A <code>═══</code> 80-char header on every file. <b>Python files only</b> — in Markdown, <code>#</code> is heading syntax and a header block renders as a stack of H1s.",
          "verbatim": true,
          "measured": "<b>992 of 992</b> class-defining files in the new tree. 100%. 3,120 of 3,999 files across the whole repo.",
          "badge": "documented",
          "note": "The caveat is scar tissue: the rule was applied to Markdown once and GitHub rendered a banner as five H1 headings."
        },
        {
          "n": 8,
          "text": "Inline comments only — no docstrings, ever.",
          "verbatim": true,
          "measured": "<b>989 of 992</b>. 99.7%, three violations.",
          "badge": "documented",
          "note": "The banner carries the purpose; trailing comments carry the reasoning. Often described as trivial to lint, and it is not: <code>ruff</code>'s pydocstyle rules <i>require</i> docstrings and there is no inverse, so this needs a custom AST check — <a href=\"../enforce/test_house_style.py\">shipped here</a>."
        },
        {
          "n": 9,
          "text": "No underscore prefix for private methods.",
          "verbatim": true,
          "measured": "<b>895 of 992</b>. 91%, <b>97 violations</b> in the new tree alone — and the JavaScript uses <code>_private</code> as a matter of course.",
          "badge": "violated",
          "note": "The rule is either Python-only and widely ignored, or it is being ignored in two languages. The source does not say, so in practice it is neither enforced nor retired."
        }
      ]
    },
    {
      "id": "security",
      "title": "Security",
      "range": "10–13",
      "blurb": "Four rules. The source groups them as 10–13 and lists them in this order; the individual numbering below is inferred from that order, not quoted.",
      "inferred_numbering": true,
      "rules": [
        {
          "n": 10,
          "text": "Evaluate is allowlist-gated — <code>JS__Expression__Allowlist</code> defaults to deny-all.",
          "verbatim": false,
          "measured": "Described in the source as having a CI guard behind it.",
          "badge": "claimed",
          "note": "The source's own table of CI guards lists five, and this is not one of them. Either the table is not the whole enforcement surface or the guard does not exist. Unresolvable from the pack alone."
        },
        {
          "n": 11,
          "text": "No arbitrary code execution — the shell-server pattern from OSBot-Playwright is explicitly not carried forward.",
          "verbatim": false,
          "measured": "A design decision, not a countable one.",
          "badge": "documented",
          "note": "A banned thing with the reason named, rather than a banned thing with quiet violations."
        },
        {
          "n": 12,
          "text": "No AWS credentials in Git. <i>If one appears in a diff, block the commit.</i>",
          "verbatim": false,
          "measured": "Not measured in the survey.",
          "badge": "documented",
          "note": "This site encodes the same rule as a release gate: <a href=\"../admin/index.html#leak\">the leak tripwire</a> fails the build on anything credential-shaped, including an AWS-account-id-shaped 12-digit number."
        },
        {
          "n": 13,
          "text": "No vault keys in Git.",
          "verbatim": false,
          "measured": "Not measured in the survey.",
          "badge": "documented",
          "note": "Also a tripwire pattern on this site: a long passphrase joined by a colon to a UUID-shaped id."
        }
      ]
    },
    {
      "id": "aws-naming",
      "title": "AWS naming",
      "range": "14–15",
      "blurb": "Both are scar tissue, both are quoted verbatim, and both name the case that motivated them. A rule that cites its own precedent is a rule people can trust.",
      "rules": [
        {
          "n": 14,
          "text": "Security group <code>GroupName</code> must NOT start with <code>sg-</code>. AWS reserves the <code>sg-*</code> prefix for security group IDs and rejects <code>CreateSecurityGroup</code> with <code>InvalidParameterValue</code>.",
          "verbatim": true,
          "measured": "Not countable — a boundary rule with an API error behind it.",
          "badge": "documented",
          "precedent": "A rejected <code>CreateSecurityGroup</code> call. The rule names the helper that implements the fix."
        },
        {
          "n": 15,
          "text": "AWS <code>Name</code> tag — never double-prefix. When the logical name already carries the namespace (e.g. <code>elastic-quiet-fermi</code>), do not wrap it again into <code>elastic-elastic-quiet-fermi</code>.",
          "verbatim": true,
          "measured": "Not countable.",
          "badge": "documented",
          "precedent": "A double-prefixed tag that shipped. The rule names the helper that implements the fix."
        }
      ]
    },
    {
      "id": "boundaries",
      "title": "Responsibility boundaries",
      "range": "16–19",
      "blurb": "Four rules, each naming exactly one owner for a capability. This is the most transferable pattern in the set, and the one that matters most in a codebase whose main author is a model: it stops a generator putting a call in a reasonable-looking wrong place.",
      "rules": [
        {
          "n": 16,
          "text": "<code>Step__Executor</code> is the ONLY class that calls <code>page.*</code> Playwright methods — with a <code>Browser__Launcher</code> carve-out for process lifecycle.",
          "verbatim": true,
          "measured": "Described in the source as enforced by a CI guard that fails the build if a raw <code>browser.new_context(</code> appears outside <code>Page__Factory</code>.",
          "badge": "claimed",
          "note": "Same discrepancy as rule 10: the source's guard table does not list this guard. This is the model the other three should follow — a boundary rule with a test behind it — if the guard is real."
        },
        {
          "n": 17,
          "text": "<code>Artefact__Writer</code> is the ONLY class that writes to sinks.",
          "verbatim": true,
          "measured": "No guard.",
          "badge": "documented"
        },
        {
          "n": 18,
          "text": "<code>Request__Validator</code> contains ALL cross-schema validation.",
          "verbatim": true,
          "measured": "No guard.",
          "badge": "documented"
        },
        {
          "n": 19,
          "text": "Routes have no logic — pure delegation to <code>Playwright__Service</code>.",
          "verbatim": true,
          "measured": "46 <code>Fast_API__Routes</code> subclasses. No guard.",
          "badge": "documented"
        }
      ]
    },
    {
      "id": "naming",
      "title": "Class and file naming",
      "range": "20–22",
      "blurb": "The three that make a symbol findable by path construction alone — no index, no grep, no re-export layer to hide behind.",
      "rules": [
        {
          "n": 20,
          "text": "Normalise <code>SGraph-AI</code> to <code>SGraph_AI</code>. Class and module names use the underscore form; repo roots and test filenames may keep the hyphen.",
          "verbatim": false,
          "measured": "The spec uses hyphenated names that are not legal Python identifiers, so the rule exists to settle the one hard case.",
          "badge": "documented"
        },
        {
          "n": 21,
          "text": "One class per file, filename identical to the class name. <i>Registries (module-level constants + helper functions, e.g. <code>STEP_SCHEMAS</code>) are the one exception — they live in a single <code>*_registry.py</code> because they are logic, not a schema.</i>",
          "verbatim": true,
          "measured": "<b>187 of 208</b> sampled files define exactly one class. 90%.",
          "badge": "violated",
          "note": "The carve-out is quoted because it is the pattern worth copying: an exception with a stated reason, rather than a rule people quietly break."
        },
        {
          "n": 22,
          "text": "<code>__init__.py</code> stays empty. <i>Never commit an empty <code>__init__.py</code> in a folder that shares a name with a sibling <code>.py</code> module: Python's import system prefers the package and every import under the module breaks.</i>",
          "verbatim": true,
          "measured": "<b>299 of 302</b>. 99%.",
          "badge": "documented",
          "precedent": "The warning in the rule is the incident. Somebody lost a day to it."
        }
      ]
    },
    {
      "id": "process",
      "title": "Process",
      "range": "23–31",
      "blurb": "Nine rules, and the source names five of them. These are agent-workflow rules rather than code style, and they belong here anyway: they are what makes a mostly-agent-written codebase safe to review.",
      "partial": "The source summarises this group rather than enumerating it. Five of the nine are named below. The remaining four are not reproduced in the brief pack, so this site does not have them and does not guess at them.",
      "rules": [
        {
          "n": "23–31",
          "text": "Human-only folders that agents must never write to.",
          "verbatim": false,
          "measured": "Named, not numbered, in the source.",
          "badge": "documented"
        },
        {
          "n": "23–31",
          "text": "The good-failure / bad-failure debrief convention.",
          "verbatim": false,
          "measured": "Named, not numbered.",
          "badge": "documented"
        },
        {
          "n": "23–31",
          "text": "Session handover.",
          "verbatim": false,
          "measured": "Named, not numbered.",
          "badge": "documented"
        },
        {
          "n": "23–31",
          "text": "Branch naming: <code>claude/{description}-{session-id}</code>.",
          "verbatim": true,
          "measured": "Named, not numbered.",
          "badge": "documented",
          "note": "The branch this site's own content was written on follows it."
        },
        {
          "n": "23–31",
          "text": "Agents never push to <code>dev</code> directly.",
          "verbatim": true,
          "measured": "Named, not numbered.",
          "badge": "documented"
        }
      ]
    },
    {
      "id": "testing",
      "title": "Testing — non-negotiable",
      "range": "T1–T4",
      "blurb": "Four rules, quoted in full, and the strongest opinion in the whole set. <b>No mocks. No patches.</b> The alternative is real in-memory composition, which is only affordable because <code>Type_Safe</code> objects are cheap to build — the type system and the testing philosophy are the same decision.",
      "rules": [
        {
          "n": "T1",
          "text": "No mocks. No patches. Use <code>register_playwright_service__in_memory()</code> and <code>in_memory_stack</code>-style composition.",
          "verbatim": true,
          "measured": "<b>4,785 tests in 81 seconds.</b> That is the evidence the position is affordable rather than aspirational.",
          "badge": "documented"
        },
        {
          "n": "T2",
          "text": "Assert on contracts — schemas, status codes, persisted artefacts — not implementation details.",
          "verbatim": true,
          "measured": "169 classes extend <code>TestCase</code>, so tests are class-based.",
          "badge": "documented"
        },
        {
          "n": "T3",
          "text": "Real Chromium for integration tests. Gate on <code>SG_PLAYWRIGHT__CHROMIUM_EXECUTABLE</code>; skip cleanly when absent.",
          "verbatim": true,
          "measured": "Not measured in the survey.",
          "badge": "documented"
        },
        {
          "n": "T4",
          "text": "Deploy-via-pytest. Deploy tests are numbered (<code>test_1__create_lambda</code>, <code>test_2__invoke__health_info</code>, …) and run top-down.",
          "verbatim": true,
          "measured": "Not measured in the survey.",
          "badge": "documented"
        }
      ]
    }
  ],
  "unwritten": {
    "blurb": "All 31 documented rules are about Python and process. JavaScript, CSS, HTML and Bash have consistent, discoverable conventions and <b>zero written rules</b>. These were reconstructed by counting. Every one of them is a candidate rule, and none of them is one yet.",
    "rules": [
      {
        "lang": "JavaScript",
        "text": "Native web components. No framework, no build step.",
        "measured": "41 <code>customElements.define</code> across 50 files. Zero bundler config.",
        "page": "../javascript/index.html"
      },
      {
        "lang": "JavaScript",
        "text": "Every component is a three-file triplet: <code>.js</code>, <code>.html</code>, <code>.css</code>, same basename, same directory.",
        "measured": "Universal in <code>components/</code>.",
        "page": "../javascript/index.html#triplet"
      },
      {
        "lang": "JavaScript",
        "text": "<code>static jsUrl = import.meta.url</code> on every component, so it can locate its own siblings.",
        "measured": "The mechanism that removes the build step.",
        "page": "../javascript/index.html#triplet"
      },
      {
        "lang": "JavaScript",
        "text": "Override <code>onReady()</code>, never <code>connectedCallback</code> directly.",
        "measured": "The base class populates the shadow root first.",
        "page": "../javascript/index.html#base"
      },
      {
        "lang": "JavaScript",
        "text": "4-space indent, single quotes, no semicolons, trailing commas in multi-line literals.",
        "measured": "3,791 indented lines a multiple of 4 vs 148 not. 4,006 single quotes vs 400 double, 91%. Semicolons split: 531 statement lines without vs 401 with.",
        "page": "../javascript/index.html#format",
        "conflict": true
      },
      {
        "lang": "CSS",
        "text": "Property values aligned to a column, per block, set by that block's longest property name.",
        "measured": "Consistent across 38 files. The unit of alignment is the block, not the file.",
        "page": "../css/index.html#alignment"
      },
      {
        "lang": "CSS",
        "text": "No literal colours outside the token file. Every colour and surface is a <code>var(--token)</code>.",
        "measured": "The single highest-value CSS rule available, and it is not written down anywhere.",
        "page": "../css/index.html#tokens"
      },
      {
        "lang": "CSS",
        "text": "Plain semantic class names. Not BEM, not utility classes — shadow DOM removes the problem both exist to solve.",
        "measured": "<code>.left-nav</code>, <code>.nav-item</code>, <code>.nav-icon</code>.",
        "page": "../css/index.html#bem"
      },
      {
        "lang": "HTML",
        "text": "2-space indent — deliberately different from the 4 used in JS and CSS.",
        "measured": "37 files.",
        "page": "../html/index.html#format"
      },
      {
        "lang": "HTML",
        "text": "Classes are for styling, <code>data-*</code> is for behaviour, and the two never mix.",
        "measured": "<code>data-view=\"compute\"</code> read as <code>btn.dataset.view</code>.",
        "page": "../html/index.html#data"
      },
      {
        "lang": "HTML",
        "text": "Every interactive control is a real control with an accessible name. No <code>&lt;div onclick&gt;</code>.",
        "measured": "ARIA on every interactive element in the sampled components.",
        "page": "../html/index.html#aria"
      },
      {
        "lang": "Bash",
        "text": "<code>set -euo pipefail</code> in every <code>.sh</code> file.",
        "measured": "<b>2 of 5</b> files have <code>set -e</code> at all, in three different forms. This is a recommendation, not an observed convention.",
        "page": "../bash/index.html#handwritten",
        "conflict": true
      },
      {
        "lang": "Bash",
        "text": "Shell is generated from a typed <code>Section__*</code> class, not written as a script.",
        "measured": "15 <code>Section__*</code> classes; <b>5 <code>.sh</code> files in 217,266 lines</b>.",
        "page": "../bash/index.html#sections"
      },
      {
        "lang": "Bash",
        "text": "Every generated section opens with a <code># ── … ──</code> banner and emits at least one bracketed <code>echo</code> breadcrumb, so boot logs stay greppable.",
        "measured": "Consistent across the 15 sections.",
        "page": "../bash/index.html#anatomy"
      },
      {
        "lang": "Prose",
        "text": "Every document is em-dash-free.",
        "measured": "<b>248 em-dashes across the 11 markdown documents that state the rule</b>, and not one file has zero. Documented, and contradicted by its own source.",
        "page": "../style/index.html#prose",
        "conflict": true
      },
      {
        "lang": "Prose",
        "text": "A CC BY 4.0 footer on every markdown document, and version-prefixed filenames: <code>v0.33.54__arch-brief__&lt;slug&gt;.md</code>.",
        "measured": "Around 1,100 files carry the footer. The filename convention is the same name-tells-you-what-it-is instinct as <code>Schema__</code> and <code>Section__</code>.",
        "page": "../style/index.html#prose"
      }
    ]
  }
}
