From 7389c1af5f91aafcd63bde9c987865c2d39b19a8 Mon Sep 17 00:00:00 2001 From: "ruleofcode.dev team" Date: Sat, 8 Aug 2026 15:50:33 +0300 Subject: [PATCH] docs: document the law card schema and its two axes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A consumer rendering law cards from `laws --json` had to infer that `stack: "frontend"` means "read satisfiedBy.angular". Nothing said so, so they coded against an observation and reported the difference as an inconsistency. It is not one — but the contract was undocumented, which is our fault, not theirs. `stack` answers selection: does this law run here at all. A `satisfiedBy` key answers guidance: which technology the instructions are written for. The two are orthogonal, and the registry proves it — no key maps onto a single stack: typescript 71 laws = 12 typescript-stack + 59 universal python 70 laws = 37 python-stack + 33 universal angular 67 laws = 58 frontend-stack + 3 typescript + 6 universal `angular` sitting on TypeScript-stack and universal laws is the clearest evidence that a key is not a stack label. Renaming it to `frontend` would make the field lie: the advice really is Angular-specific, and a React project matches `stack: "frontend"`. The JSON already ships the mapping as `satisfiedByStacks.stackToKey`, so the section points renderers at that field rather than a table to copy — a key added later is then picked up rather than missed. It also states the gap plainly: all 58 frontend laws carry only Angular guidance, and a card should say so rather than present Angular steps as universal (#12). --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/README.md b/README.md index 85e757a..a63771d 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,72 @@ stack do not run and are not counted: The project type comes from `project.type` in the config, or is detected from the filesystem. +## 🗂️ Law card schema (`roc laws --json`) + +`roc laws --json` is the contract for anything that renders a law — a docs site, an +IDE panel, a dashboard. It is versioned by `schemaVersion` (currently `2`). + +```jsonc +{ + "schemaVersion": 2, + "tool": "ruleofcode", // the package name this build was published under + "version": "7.17.5", + "registryTotal": 173, + "satisfiedByStacks": { // the mapping described below — read it, don't hardcode it + "keys": ["typescript", "angular", "python"], + "stackToKey": { "frontend": "angular", "typescript": "typescript", "python": "python" } + }, + "laws": [ /* … */ ] +} +``` + +### `stack` and `satisfiedBy` are two different axes + +This is the part that looks like an inconsistency and is not: + +| field | question it answers | values | +|---|---|---| +| `stack` | **selection** — does this law run on this project at all? | `frontend`, `typescript`, `python`, or absent (universal) | +| `satisfiedBy` keys | **guidance** — which technology are these instructions written for? | `typescript`, `angular`, `python` | + +**They are orthogonal. A `satisfiedBy` key is not a stack label**, and no key maps 1:1 +onto a stack. Measured against the shipped registry: + +| key | laws | spread across stacks | +|---|---|---| +| `typescript` | 71 | 12 `typescript` + 59 universal | +| `python` | 70 | 37 `python` + 33 universal | +| `angular` | 67 | 58 `frontend` + 3 `typescript` + 6 universal | + +`angular` appearing on TypeScript-stack and universal laws is the clearest evidence: +it names the technology the advice is written in, not the projects the law applies to. + +**Render by the key, not by the stack.** `satisfiedByStacks.stackToKey` gives the +default key for a stacked law; universal laws carry whichever keys have been authored. +Read that object rather than hardcoding a table — if a key is ever added, your renderer +picks it up. + +### Where the guidance is missing, say so + +All 58 `frontend` laws carry only `satisfiedBy.angular`. **There is no React or Vue +guidance yet** ([#12](https://github.com/Rule-Of-Code/RoC/issues/12)). A React project +matches `stack: "frontend"`, so those laws run — but the only remediation text we have +is Angular's. + +If you render law cards, say that plainly ("written for Angular — no React/Vue +instructions yet") rather than presenting Angular steps as if they were universal. The +same honesty `detectionLimits` applies to detection, applied to remediation. + +### The other authored fields + +| field | what it is | +|---|---| +| `rationale` | why the law exists — written after reading the real detector | +| `detectionLimits` | what the detector does **not** catch. A law with none silently claims to be comprehensive | +| `configKeys` / `identityKeys` | every spelling that resolves this law in `laws.severity` / `notApplicable` / `enabled` | +| `urlSlug` | a registry-unique, URL-safe segment for `/laws/:slug` | +| `severity` | the authored default, before any config override | + ## 🔧 Configuration (`ruleofcode.config.json`) ```jsonc