# House Style > Paste the copy an organisation has already published. Discover the voice those samples are > actually written in, codify it into a house style guide a stranger could apply, and enforce that > guide against one new draft line by line. Runs at https://house-style.skillsafe.ai/ ## What it is for A team's writing drifts because nobody wrote down what it sounds like. House Style closes that loop in one sitting over one work object — the prose the organisation publishes: 1. **discover** — read the samples, describe the voice. Dimensions with verbatim evidence, the vocabulary the writing reaches for and the vocabulary it avoids, and what the voice never does. 2. **codify** — turn that into a guide: principles, rules with worked do/not pairs, a terminology table, a mechanics quick-reference and a reviewer checklist. 3. **enforce** — hold a new draft to the guide. Every span that breaks a rule comes back quoted, with the rule id, why it matters and the sentence rewritten, plus the whole draft rewritten and a 0-100 score. Lane A's output pre-fills lane B: the voice profile is handed to the guide lane, and the guide's `style_rules` JSON is loaded straight into the enforcement lane's guide box. ## The free lane, in the browser Before anything is sent, `stylescan.js` measures the paste locally. No network, no account, no credits: - splits the paste into samples on `---` separators, `## ` headings or blank-line blocks - tokenises sentences with abbreviation, decimal and paragraph-boundary handling - words, sentences, paragraphs, mean/median/spread of sentence length, longest and shortest - Flesch reading ease and Flesch-Kincaid grade - passive-voice detection (be-verb plus participle, including irregular participles) - contraction rate, first/second/third person counts - exclamations, questions, em dashes, semicolons, ellipses, parentheticals - hedges, intensifiers, adverb rate, ALL-CAPS runs, emoji - repeated sentence openers, heading case (Title Case against sentence case) - serial-comma use, detected over real three-item lists rather than by counting ", and" - terminology variants (email/e-mail, setup/set-up, ...) and spelling splits (-ize/-ise, color/colour, license/licence, ...) - interchangeable-marketing vocabulary, each mapped to its plainer replacement - credential-shaped literals — addresses, phone numbers, API keys, signed links — masked with `[EMAIL-1]`-style placeholders before anything leaves the browser Every measurement that crosses a threshold becomes a flag with a stable id, and the prompt requires the model to confirm, clear or set aside every one of those ids. The reconciliation table names any it missed and any it invented. ## Input contract `POST https://api.skillsafe.ai/v1/app-api/run` (or `/run-stream`). The app is identified by the bearer token, not by a path segment, and the input object **is** the request body - it is not wrapped in an `input` key: | Field | Type | Notes | | --- | --- | --- | | `task` | string | `discover`, `codify` or `enforce`. Required — it routes the prompt. | | `samples` | string | The work object: published copy, or the draft under review. | | `guide` | string | The house style guide. Required for `enforce`; ignored elsewhere. Markdown, or the `style_rules` JSON. | | `audience` | string | Who the writing is for. | | `channel` | string | `general`, `web`, `product`, `docs`, `email`, `social`, `support`. | | `register` | string | `auto`, `formal`, `neutral`, `conversational`, `playful`. | | `notes` | string | Anything the samples cannot say — a spelling decision, a banned word. | | `prescan` | object | The browser's measurements, including `flags[]` with ids. | | `upstream` | string | The previous lane's digest, when a handoff was taken. | ## What happens after the model replies - **The rewrite is put back through the free scanner.** The enforce lane's artifact is a rewritten draft, so the same browser measurements that judged the copy going in are run over the copy coming out and shown side by side — passive-voice rate, marketing terms, reading grade, mean sentence length and the flag count, with the direction of each change marked. Zero credits, and it is the browser's own measurement rather than the model's opinion of its own work. - **The findings are applied, not just listed.** Every enforce finding carries a `quote` that must be a verbatim span of the draft and a `rewrite` that replaces it, so the patch is machine-applicable: one button applies every rewrite to the copy in the box, each finding can be applied on its own, and the free read re-runs over the patched text immediately. A quote the browser cannot find verbatim in the box is labelled as such instead of being silently trusted — which is the only enforcement the verbatim-quote rule has. - **A guide too large to send is cut structurally, never mid-JSON.** A `style_rules` guide over the send budget has whole terminology entries and then whole rules dropped from the end, is re-serialised so it still parses, and carries a `_truncated` object stating how many were dropped; the model is told to enforce what it was given and never to invent the rest. A guide too large even after that is refused before the run rather than sent as a broken fragment. ## Output contract One JSON object, same envelope in every lane: `lane`, `title`, `verdict`, `headline`, `summary`, `traits[]`, `rules[]`, `terms[]`, `findings[]`, `artifact`, `artifact_json`, `coverage_check[]`, `questions[]`, `confidence`. - `verdict` — discover: `distinct` | `emerging` | `generic` | `inconsistent`; codify: `ready` | `provisional` | `needs-input`; enforce: `on-voice` | `drifting` | `off-voice`. - `findings[]` — `{id, severity, rule_id, quote, why, rewrite}`. `quote` is a verbatim span of the input; `rewrite` is a full replacement, or `""` when the span should be deleted. - `artifact_json` — `voice_profile` (discover), `style_rules` (codify), `voice_report` (enforce). The app parses and validates it before showing it, and shows the parse error rather than a guide the next lane would silently accept. - `coverage_check[]` — one entry per prescan flag id, `confirmed` | `cleared` | `not-applicable`. ## Model and cost Model `gpt-terra` (currently `gpt-5.6-terra`), publisher markup 1000 bps. `estimate` is free and returns the per-lane hold; the app compares it against the account balance and disables the run button rather than letting a run 402. The prescan, the exports and the bundled example runs cost nothing. ## Sources Derived from three open-source Agent Skills in `anthropics/knowledge-work-plugins`: `@anthropics/discover-brand` (discover), `@anthropics/guideline-generation` (codify) and `@anthropics/brand-voice-enforcement` (enforce, and the primary skill). This app is a derived work and is not affiliated with those authors. ## Pages - `/` — the app - `/api.html` — the API tutorial, tabbed across cURL, Python, JavaScript, Go, Java, Ruby, PHP and C# - `/tokens.html` — token management (noindex)