# Layered Style Guidance Enforcement—Precedence Resolution Algorithm
#
# This file defines how the CSA backend (and any other consumer of the
# conflict register) determines which rule value applies to a given piece of
# content when multiple layers have overrides.
#
# See _data/rules/schema.yaml for the rule entry structure.
# See docs/layered-enforcement.md for the conceptual model + worked examples.

algorithm_version: "1.0.0"

# -----------------------------------------------------------------------------
# Resolution algorithm
# -----------------------------------------------------------------------------
algorithm: |
  Given:
    - rule R from conflict-register.yaml
    - content C with attributes { persona, format, platform, publication, distribution }
    - precedence order P = [platform, format, persona, general] (highest first; redefinable per-rule)

  Resolve R for C:
    1. For each layer L in P, in order from highest to lowest precedence:
       a. Filter R.overrides to those whose selector matches C's attributes for layer L.
          (A selector matches if every key in the selector is present in C's attributes
          and values are equal. AND-of-keys; equality only in v1.0.)
       b. If exactly one override matches at this layer:
            return that override's value (resolution complete).
       c. If multiple overrides match at this layer:
            collect them and apply tie-breakers (see `tie_breakers` below).
            If still ambiguous after tie-breakers:
              raise an editor-resolution event; do not silently pick one.
       d. If no override matches at this layer:
            continue to the next lower layer.
    2. If no override matches at any layer:
       return R.default.value.

# -----------------------------------------------------------------------------
# Tie-breakers—applied when multiple overrides at the same layer match.
# -----------------------------------------------------------------------------
tie_breakers:
  - rank: 1
    name: most_specific_selector
    description: |
      The override with the most selector keys wins. Example: a rule with
      override { format: everything-to-know, platform: apple-news } beats an
      override with { platform: apple-news } when both match the content.
  - rank: 2
    name: most_recent_introduction
    description: |
      If selectors are equally specific, the override introduced in the most
      recent csa-content-standards version wins. Use the `introduced` field on
      the rule entry, falling back to git-blame if unspecified.
  - rank: 3
    name: data_validated_wins
    description: |
      If still tied, prefer the override with `data_validated: true` over
      one without. Performance-backed rules outrank judgment-based rules.
  - rank: 4
    name: editor_resolution
    description: |
      If still tied after the above three, the system surfaces the conflict
      to a human editor. Do not silently choose. Engineering should log the
      conflict + the candidate overrides + the content's attributes for
      followup register cleanup.

# -----------------------------------------------------------------------------
# Special cases
# -----------------------------------------------------------------------------
special_cases:
  null_override:
    description: |
      An override may set `value: null` to mean "this layer removes the
      higher-layer rule entirely." Useful when, e.g., a platform explicitly
      retracts a general-layer constraint.
    example:
      rule_id: headline.intro_word_count
      default: { value: "80-120 words", source: "(historical)" }
      overrides:
        - selector: { layer: general }
          value: null
          source: docs/changelog.md §1.8.1
          rationale: SEO team broadly retracted the 80-120 intro word count rule (2026-04-23)

  additive_only_rule:
    description: |
      A rule whose `default.value` is null and whose overrides introduce
      constraints not present at the general layer. Common for format-specific
      structural requirements (e.g., FAQ sister-rule heds, Recipe dek format).
    example:
      rule_id: format.recipe.intro_word_count
      default: { value: null, source: null }
      overrides:
        - selector: { format: recipe }
          value: "150-350 words"
          source: docs/recipe.md
          kind: additive

  per_rule_precedence_override:
    description: |
      A rule may redefine its `precedence` list to handle special cases—e.g.,
      a quality-standard rule that should NEVER be overridden by a platform
      (Layer 0 Constitution + Layer 1 Quality from CSA backend's Trust
      Hierarchy). For those rules, set `precedence: [general]` to indicate no
      override is allowed.
    example:
      rule_id: compliance.no_unverified_facts
      default: { value: "all facts must be verified; links must point to reputable sources only", source: "docs/brand-guidelines.md §1.8" }
      overrides: []
      precedence: [general]
      note: This rule is system-wide-inviolable; no platform / format / persona may override.

# -----------------------------------------------------------------------------
# Implementation hint for the StyleGuideComposer
# -----------------------------------------------------------------------------
composer_extension:
  current_state: |
    Per backend/style_guides/README.md, the StyleGuideComposer currently loads
    in trust-hierarchy order:
      1. Layer 0/1 system guides (constitution, quality standards)
      2. Layer 2 platform-specific voice guides (from platform mapping)
      3. Layer 2 organization-specific voice guides (from org record)
    Persona and Format are NOT separate layers in the current Composer—they
    are folded into Platform/Org or absent.

  proposed_extension: |
    To support the editorial 4-layer hierarchy (General → Persona → Format →
    Platform), the Composer should walk:
      1. Layer 0 Constitution (inviolable)
      2. Layer 1 Quality + General Guidance (the "general" rules)
      3. Persona (from CSA Target Audience selection at draft time)
      4. Article Format (from Format selector at draft time)
      5. Platform / Publication (from destination)
    For each rule that has overrides at multiple layers, apply this
    precedence-resolution algorithm. The LAST applicable layer's value wins
    (where "last" = highest rank in P = [platform, format, persona, general]
    walked from highest to lowest).

  prompt_assembly: |
    When assembling the prompt for the writer agent:
      - Constitution + Quality + General rules go in first (rarely overridden)
      - Resolved (per-rule) values from each rule's effective override go in next
      - Per-piece editorial direction (Layer 3) goes in last
    Avoid loading every layer's rules verbatim—the precedence resolver
    eliminates contradictions before prompt assembly. Verbatim-stacking
    contradictory rules confuses the LLM and reintroduces the runtime conflict
    the register exists to prevent.

# -----------------------------------------------------------------------------
# Enforcement-target routing
# -----------------------------------------------------------------------------
enforcement_routing:
  description: |
    Each rule entry's `enforcement_target` field tells the system who applies
    the rule. Engineering should route rules accordingly:
  routes:
    writer: |
      Rule text becomes part of the agent prompt for the drafting agent. Best
      for prose rules requiring judgment (voice, tone, persona framing,
      "lead with empathy then offer solutions").
    grader: |
      Rule is checked by programmatic post-hoc validation (regex /
      parse-tree analysis / character count). Best for machine-evaluable rules
      (em dash spacing, Oxford comma presence, headline char count, single vs
      double quotes in heds, presence of required CMS fields).
    composer: |
      Rule is applied at composition time. Determines which guides to load,
      which agent prompt template to use, what context window allocations
      apply. Examples: trust-hierarchy size limits (Layer 2 ≤5K tokens),
      AGENT-AUDIENCE filtering for the headline-only agent vs the
      general-style agent.
    human: |
      Rule requires human editorial review. Engineering surfaces it via
      reviewer UI as a checklist item; not enforced programmatically. Best
      for AI disclosure presence, byline accuracy, helpful-content audit,
      breaking-news triage.

# -----------------------------------------------------------------------------
# Validation timing
# -----------------------------------------------------------------------------
validation_timing:
  write_time:
    description: When the agent generates a draft, machine-evaluable rules are checked + corrected. Failure prevents the draft from advancing
    rules: 'All `grader`-target rules with `machine_evaluable: true`'
  composition_time:
    description: When the Composer assembles a prompt, layer-precedence is resolved + the right rules are loaded
    rules: All `composer`-target rules
  review_time:
    description: When a human reviewer reads the draft, judgment-required rules are surfaced as a checklist
    rules: All `human`-target rules + `prose`-type rules that the LLM may have applied incompletely
  publish_time:
    description: Final pre-publish gate. Compliance rules with no exceptions checked one last time
    rules: 'System-wide compliance rules (precedence: [general])'
