Layered Enforcement
Scope: This page defines the conceptual model and implementation contract for layered style guidance enforcement across McClatchy CSA. It is the human-readable companion to the machine-readable artifacts at
_data/rules/schema.yaml,_data/rules/precedence.yaml, and_data/rules/conflict-register.yaml. Engineering ingests the machine-readable files; humans (editors + product) work from this page.
11.1 The Four-Layer Model
Style guidance lives in four layers, each more specific than the last. When two layers say different things, the lower (more specific) layer wins.
| Layer | Rank | What it covers | Where it lives |
|---|---|---|---|
| General | 1 (lowest) | Universal rules across all McClatchy content. AP-Compatible is the default backbone | §1 General Guidelines · §2 Headlines · §6 Publishing Guidelines · §10.6 AP-Compatible |
| Persona | 2 | Audience-specific overrides | §4 Personas (Discover Browser, Curious Optimizer, Watercooler Insider, Curious Explorer, Wonder-Driven Science Enthusiast) |
| Article Format | 3 | Format-specific overrides | §3 Article Formats (Everything to Know, FAQ, What to Know Next; pending: Recipe, Timeline, Interview, Recap, Fan Theory / Fan Question, Obituary, Couple / Baby, Cast Introduction / Update; retired: Discover Explainer) |
| Platform | 4 (highest) | Distribution-platform + per-publication overrides | §10 Platform Guidance (SmartNews, Apple News, Us Weekly, Trend Hunter B2C, Woman’s World, AP-Compatible) |
Default precedence (highest first): [platform, format, persona, general]. A rule may redefine its precedence—e.g., system-wide compliance rules use precedence: [general] to indicate that no override is allowed.
11.2 How a Rule Resolves
For a piece of content with attributes { persona, format, platform }:
- Walk the rule’s
overridesarray in precedence order, highest layer first - The first override whose
selectormatches the content’s attributes wins - If no override matches at any layer, the rule’s
default.valueapplies - If two overrides at the same precedence layer both match, apply tie-breakers (most-specific selector → most-recent introduction → data-validated wins → editor resolution)
The full algorithm is in _data/rules/precedence.yaml.
11.3 Worked Examples
Example 1—Em dash spacing on a Woman’s World piece
Rule: punctuation.em_dash_surrounding_spaces (boolean)
| Layer | Value | Source |
|---|---|---|
| General | true (spaces on both sides—AP standard) |
docs/ap-compatible-quick.md + docs/ap-compatible-condensed.md (Em Dash) |
| Persona | (no override) | — |
Format (everything-to-know) |
(no override) | — |
Platform (womans-world) |
false (no spaces; AMI house-style deviation from AP) |
docs/womans-world.md §10.5 |
Resolution: the platform override matches → em dashes have no spaces for this piece. (USW restates the AP default explicitly with kind: restate; only WW deviates.)
Example 2—Couples-verb agreement on a Woman’s World cast piece
Rule: grammar.couple_verb_agreement (enum: singular |
plural) |
| Layer | Value | Source |
|---|---|---|
| General | singular (AP collective-noun treatment) |
docs/ap-compatible-condensed.md |
| Persona | (no override) | — |
Format (cast) |
(no override) | — |
Platform (womans-world) |
singular (AMI default; mashups always plural) |
docs/womans-world.md §10.5 |
Resolution: platform override matches but does not change the value → couples take singular verbs. The override is restated for clarity (Woman’s World is the AMI default; Us Weekly is the AMI exception that flips to plural).
Example 3—Headline character count on an FAQ for the Curious Optimizer published to Apple News
Rule: headline.char_count (range)
| Layer | Value | Source |
|---|---|---|
| General | 80–100 |
docs/brand-guidelines.md §1.2 |
Persona (curious-optimizer) |
(no override) | — |
Format (faq) |
(no override) | — |
Platform (apple-news) |
90–120 (sweet spot 110–119, data-validated) |
docs/platform-apple-news.md §10.2 |
Resolution: platform override matches → 90–120 characters, target 110–119. Even though the content is also an FAQ for the Curious Optimizer, no rule at those layers contradicts the platform’s range.
Example 4—Headline char count on an Everything-to-Know with no platform specified
Rule: headline.char_count (range)
| Layer | Value | Source |
|---|---|---|
| General | 80–100 |
docs/brand-guidelines.md §1.2 |
| Persona | (no override) | — |
Format (everything-to-know) |
80–150 |
docs/everything-to-know.md |
| Platform | (no destination yet) | — |
Resolution: no platform override applies → format override matches → 80–150 characters. ETK heds may run longer than the universal default.
Example 5—Word “God” in a Woman’s World recipe piece
Rule: faith.god_word_permitted (enum)
| Layer | Value | Source |
|---|---|---|
| General | editorial-judgment |
(no general guidance) |
| Persona | (no override) | — |
Format (recipe) |
(no override) | — |
Platform (womans-world) |
permitted-only-in-faith-pieces |
docs/womans-world.md §10.5 |
Resolution: platform override applies. Critical clause: “Apply faith-related guidelines only when content is explicitly about faith. Never force faith themes into unrelated content.” A recipe is not a faith piece → “God” is not used here despite WW permitting it elsewhere.
Example 6—Three layers all weigh in on a constraint
A Trend Hunter B2C piece in the FAQ format targeting the Curious Optimizer persona, distributed via Apple News:
| Rule | General | Persona | Format | Platform | Resolution |
|---|---|---|---|---|---|
voice.audience_framing |
either | psychographic-only | (none) | psychographic-only | psychographic-only (persona + platform agree; either alone would resolve the rule) |
headline.char_count |
80–100 | (none) | (none) | 90–120 (apple-news) | 90–120 (platform wins) |
body.faq_block_word_count |
(none) | (none) | 100–200 words | (none) | 100–200 words (format adds; no other layer addresses it) |
voice.banned_slang |
(none) | (none) | (none) | TH B2C: [slay, bestie, vibes] | banned list applies (additive) |
This is the typical case—different rules resolve at different layers, none of which conflict. The architecture is designed so layers stack without erasing each other unless they intentionally override.
11.4 Override Kinds
A rule’s per-layer override can be one of three kinds:
| Kind | Meaning | Example |
|---|---|---|
override |
Layer changes the value already set at a higher (lower-rank) layer | USW flips couples-verb from singular → plural |
additive |
No higher-layer value exists; the layer introduces a constraint | FAQ format adds body.faq_block_word_count: 100–200 (no general rule) |
restriction |
Layer narrows the scope of an existing rule | Apple News adds AI-disclosure requirements on top of the general AI disclosure |
A value: null on an override means “this layer removes the rule entirely.” Used rarely—see §1.8.1 retraction of the 80–120 word intro rule for the pattern.
11.5 Reconciliation with the CSA Backend (current state vs editorial model)
There is an important conceptual gap between the editorial model described above (lower layer overrides higher layer on conflict) and the CSA backend’s current implementation (all layers stack; no override semantics). Engineering’s own documentation makes this explicit.
Per the CSA engineering Confluence page “How Style Guides Work” (PGS space, page 1949663238, last updated 2026-05-05):
“Style guides are composed in four stacked layers—none override each other, they all stack together”
This is the load-bearing sentence. The current implementation is stacking, not overriding. Whatever each layer contributes is concatenated into the prompt; conflicts are left for the LLM to reconcile at runtime, non-deterministically.
CSA Engineering’s Current Trust Hierarchy
| Layer | Trust Level | What It Is | Example |
|---|---|---|---|
| 0 | Constitution | Journalism ethics—always present, never overridden | Quote integrity, no hallucination |
| 1 | Quality | Content quality standards—always present | AP style, anti-slop rules |
| 2 | Voice | Platform + organization voice guides | Woman’s World tone, Charlotte Observer brand voice |
| 3 | Editorial | Per-article editor notes | “Lead with the economic angle” |
How CSA Currently Resolves Style Guides for a Piece
Per the same Confluence page, three inputs determine which guides get loaded for a given draft—and all three contribute independently to Layer 2 stacking:
- Email domain (indirect)—the user’s email maps to an organization at login (e.g.,
@usmagazine.com→ US Weekly org). The org ID drives which Layer 2 organization voice guides load - Assigned organization (direct)—admin can override a user’s org assignment via the admin panel (e.g., Jane at
@usmagazine.commanually assigned to Woman’s World). Email-level override always wins over domain-level - Platform Distribution step (direct)—when a writer selects a platform formatter (Local News, Woman’s World, US Weekly, etc.), that platform name drives which Layer 2 platform voice guides load
The org-assignment and platform-selection inputs are independent. Org override changes which org-voice guides load; platform selection changes which platform-voice guides load. Both Layer 2 sub-stacks load simultaneously, and engineering acknowledges they can conflict—e.g., Jane gets assigned to Woman’s World but selects US Weekly in Platform Distribution → she gets WW org voice AND USW platform voice in the same prompt.
Where Editorial Model and Current Implementation Diverge
| Editorial concept | Where in editorial model | Where in CSA backend today | Implementation gap |
|---|---|---|---|
| Universal rules | Layer 1 (General) | L0 Constitution + L1 Quality | maps cleanly |
| Audience persona | Layer 2 (Persona) | not modeled—no persona-specific guide loading | gap—Persona is editorial-side only |
| Article format | Layer 3 (Format) | not modeled—style_guide field exists in code per Confluence but is “informational only and is NOT currently used to apply style guides automatically” |
gap—Format is editorial-side only |
| Platform / publication | Layer 4 (Platform) | L2 Voice (platform sub-stack + org sub-stack, both load) | exists but stacks rather than overrides |
| Per-piece direction | not in editorial model | L3 Editorial | exists; orthogonal to the layered model |
| Conflict resolution semantics | Lower layer overrides higher | No override; all layers stack | fundamental semantic gap |
National Team Gap (from same Confluence page)
A @mcclatchy.com user creating content for multiple publications has a specific problem in the current system:
- Email maps to McClatchy corporate org at login (permanent org assignment)
- Per-request
organization_idcan be passed for a specific child publication (Charlotte Observer, Sacramento Bee, etc.) → composer fetches that child org’s voice guides - But: National Team users aren’t using the Platform Distribution step → they get no platform-specific voice guides, only system constitutional + quality standards + (maybe) McClatchy corporate org voice
- Net result: National Team gets the most generic style-guide composition possible. If they’re writing for the Charlotte Observer, nothing in the current style-guide system tells the LLM to sound like the Charlotte Observer.
This is a real gap engineering has named. The layered enforcement register described on this page closes that gap—it makes Persona, Format, and Platform first-class layers, regardless of how the user reached the content.
Proposed Composer Extension
To honor the editorial 4-layer hierarchy, the Composer should walk:
- L0 Constitution (inviolable; rules with
precedence: [general]and emptyoverrides) - L1 Quality + General Guidance (the rule defaults—
default.value) - Persona (from CSA Target Audience selection at draft time—overrides matching
selector: { persona: ... }) - Article Format (from Format selector at draft time—overrides matching
selector: { format: ... }) - Platform / Publication (from destination + Platform Distribution selection—overrides matching
selector: { platform: ... }or{ publication: ... })
For each rule, the precedence-resolution algorithm in _data/rules/precedence.yaml returns the effective value for the piece. The Composer assembles the prompt using only resolved values—not by stacking every layer’s text verbatim. Verbatim-stacking contradictory rules is the failure mode the register exists to prevent.
This proposal asks engineering to add override semantics to Layer 2 (and to promote Persona + Format to first-class layers). The current “all stack, none override” approach leaves the LLM to reconcile conflicts at runtime, and its resolution is non-deterministic: a piece run twice can resolve the same conflict differently. Override semantics, executed at composition time, eliminate that variance.
How CSA Selects the AP Tier
Engineering’s audit doc (style-guides-for-pierce-audit.md, 2026-05-08) confirms the CSA Composer automatically selects which AP-Compatible tier to load based on content type:
| Content type | AP tier loaded | Token cost |
|---|---|---|
| Breaking news / high-volume batch | Quick | ~2K |
| Most situations (default) | Condensed | ~12K |
| Investigative / features / in-depth analysis | Thorough | ~25K |
This means the AP-Compatible page hierarchy in §10.6 maps directly to a runtime decision: pick the tier that matches the content type. The Quick / Condensed / Thorough split is a token-economy decision the Composer makes per piece, not an editorial preference. AP applies to all platforms (it’s the always-on Layer 1 quality backbone underneath every Layer 2 voice guide), but only one tier is loaded at a time.
Implementation Routing
Each rule’s enforcement_target field tells engineering where the rule applies:
writer→ rule text becomes part of the agent prompt that drafts content; LLM applies it during generation. Best for prose rules requiring judgment (voice, tone, persona framing)grader→ rule is checked by programmatic post-hoc validation (regex, parse-tree analysis, char-count). Best for machine-evaluable rules (em-dash spacing, Oxford comma presence, single vs double quotes in heds)composer→ rule is applied at composition time; determines load order, AGENT-AUDIENCE filtering, context-window allocation, layer precedencehuman→ rule requires editorial review; surfaces in reviewer UI as a checklist item
11.6 What Engineering Receives
When CSA engineering ingests the layered enforcement system, they receive:
Three machine-readable files (canonical)
| File | Purpose |
|---|---|
_data/rules/schema.yaml |
Defines what a rule entry looks like |
_data/rules/precedence.yaml |
Defines the resolution algorithm + tie-breakers + Composer extension proposal |
_data/rules/conflict-register.yaml |
Every known cross-layer rule + its overrides |
All three are downloadable from the same-origin Cloudflare-Access-gated /assets/sources/rules/ path (see build-downloads.sh for the publishing mechanism).
Two human-readable docs (companion)
| Page | Purpose |
|---|---|
| Layered Enforcement (this page) | Conceptual model + worked examples + reconciliation with CSA backend Trust Hierarchy |
| Conflict Register (human view) | Every rule + its overrides, formatted as readable Markdown tables |
11.7 What This Doesn’t Solve (Yet)
- The override-vs-stacking implementation gap. The register and precedence algorithm assume override semantics. The current CSA backend stacks. Until engineering extends the Composer (§11.5), the register’s resolved values won’t be reflected in actual prompts. Today the register is documentation + audit reference; tomorrow it becomes the runtime contract
- LLM-judgment rules. Rules with
machine_evaluable: false(voice, tone, persona framing, helpful-content audit) cannot be programmatically validated. They must reach the writer agent’s prompt and the human reviewer’s checklist. The register tells engineering which rules are which - National Team Layer 2 gap. A
@mcclatchy.comuser not going through Platform Distribution gets no platform-specific voice guides today (per engineering’s Confluence doc). The register is structurally ready to address this—it lets the system route byselector: { publication: ... }from any source—but engineering needs to wire the National Team workflow to set that selector style_guidefield unused. Per engineering’s Confluence: “this field exists in the code but is informational only and is NOT currently used to apply style guides automatically.” Wiring this field is a prerequisite for format-driven guide loading- Email override × Platform selection conflict case. If admin assigns Jane (
@usmagazine.com) to Woman’s World, and Jane selects US Weekly in Platform Distribution, she gets WW org voice + USW platform voice stacked. Engineering knows this can conflict. Once override semantics land (§11.5 proposal), the precedence algorithm resolves this deterministically—{ publication: us-weekly }selector matches → USW platform values win. Until then, the LLM resolves it non-deterministically at runtime - Selector grammar limits. v1.0 supports equality + AND-of-keys only. Rules requiring OR / NOT / regex matching are out of scope until v1.1
- Per-piece editorial direction (L3). The Composer’s per-piece editorial direction layer is orthogonal to this register. It is per-piece session state, not part of the layered guidance model
- Cross-rule interactions. Some rules interact (e.g.,
headline.char_countinteracts withheadline.front_load_keywords—long heds make front-loading harder). The register listsrelated_rulesper entry but doesn’t yet model the interaction graph - Override priority within a layer. A piece may have two platforms (rare—e.g., USW content distributed to Apple News). v1.0 surfaces the conflict to an editor; future versions may model platform-of-platform precedence
11.8 Maintenance Protocol
When a new rule is established or an existing rule changes:
- Author or update the canonical doc page (e.g.,
docs/<format>.md,docs/<platform>.md) - Add or update the rule entry in
_data/rules/conflict-register.yamlwith the appropriatedefaultandoverrides - Update the human-readable twin at
docs/conflict-register.md(hand-maintained mirror in v1.0) - Bump csa-content-standards version + add changelog entry
- Re-run
scripts/build-downloads.shto regenerate downloadable copies inassets/sources/andassets/rules/ - CSA engineering pulls the updated YAML on next ingest cycle
11.9 Reference Hierarchy
When this guide doesn’t address an enforcement question, defer in order:
- The relevant doc page (e.g.,
docs/<format>.mdordocs/<platform>.md) _data/rules/conflict-register.yamlfor the resolved value_data/rules/precedence.yamlfor resolution semantics_data/rules/schema.yamlfor structural questions about the rule entry shape- CSA backend
backend/style_guides/README.mdfor the Trust Hierarchy + Composer behavior docs/ap-compatible.mdfor the underlying journalism standard