#Campaigns

#The Problem

Real organisations have dozens or hundreds of expectations. A CISO's expectation that "all services are secure" is not a single check — it depends on sub-expectations about patching, access controls, encryption, and monitoring. These expectations form a natural hierarchy, and evaluating them all at once is both slow and hard to report on.

#How WRIT Handles It

WRIT organises expectations into campaigns using the step towards clause. A campaign is an expectation at the top of a hierarchy. Sub-expectations declare that meeting them is a "step towards" meeting the campaign.

#Campaign Structure

graph TD
  A["All services are secure"] --> B["All services are patched"]
  A --> C["All services have access controls"]
  A --> D["All services are encrypted"]
  B --> E["Critical patches applied<br/>within 7 days"]
  B --> F["Non-critical patches applied<br/>within 30 days"]
  C --> G["No shared accounts on<br/>production services"]
  style A fill:#ecfeff,stroke:#06B6D4,color:#0e7490
  style B fill:#ecfeff,stroke:#06B6D4,color:#0e7490
  style C fill:#ecfeff,stroke:#06B6D4,color:#0e7490
  style D fill:#ecfeff,stroke:#06B6D4,color:#0e7490
  style E fill:#ecfeff,stroke:#06B6D4,color:#0e7490
  style F fill:#ecfeff,stroke:#06B6D4,color:#0e7490
  style G fill:#ecfeff,stroke:#06B6D4,color:#0e7490

#DSL Syntax

A campaign is an expectation whose step towards is "__CAMPAIGN__":

expectation: "all_services_secure"
    expector: "all_stakeholders" ? "id" = "CISO"
    partition: "is_secure" of "all_services" of "ITService"
    expect empty: "is_false"
    expectee: SELF->"owner"
    actions:
        - "Review and remediate security posture"
    step towards: "__CAMPAIGN__"

Sub-expectations reference their parent:

expectation: "all_services_patched"
    expector: "all_stakeholders" ? "id" = "PATCH_LEAD"
    partition: "is_patched" of "all_services" of "ITService"
    expect empty: "is_false"
    expectee: SELF->"owner"
    actions:
        - "Apply outstanding patches"
    step towards: "all_services_secure"

#Scoped Evaluation

All evaluation in WRIT is scoped to a campaign. When you evaluate a campaign, WRIT:

  1. Finds the campaign expectation.
  2. Collects all expectations that are steps towards it (direct and transitive).
  3. Builds a scoped registry containing only the entity types, entity sets, and data relevant to those expectations.
  4. Evaluates all expectations within the scoped registry.

This scoping means:

  • Faster evaluation — Only relevant data is loaded and processed.
  • Clearer reports — Results are grouped by campaign, not scattered across the entire domain.
  • Independent progress — Different teams can focus on different campaigns.

#Campaign Status

A campaign's overall status is derived from its sub-expectations:

Sub-expectation Results Campaign Status
All met (true) Met — the campaign goal is achieved
Any not met (false) Not met — at least one expectation has failures
Any unknown, none false Uncertain — data gaps prevent a definite answer

#Worked Example

A simple two-level campaign for service ownership:

expectation: "ownership_campaign"
    expector: "all_stakeholders" ? "id" = "CISO"
    partition: "has_owner" of "all_services" of "ITService"
    expect empty: "is_false"
    expectee: SELF->"owner_lead"
    actions:
        - "Review service ownership programme"
    step towards: "__CAMPAIGN__"

expectation: "owners_contactable"
    expector: "all_stakeholders" ? "id" = "SERVICE_MGMT"
    partition: "owner_has_email" of "owned_services" of "ITService"
    expect empty: "is_false"
    expectee: SELF->"owner"
    actions:
        - "Update contact details for service owner"
    step towards: "ownership_campaign"

Evaluating ownership_campaign:

  • First checks that all services have owners.
  • Then checks that all owned services have contactable owners.
  • Reports are grouped under the campaign, showing progress towards the overall goal.

#Common Pitfalls

  • Circular step-towards references — Expectation A steps towards B, and B steps towards A. This creates an infinite loop. WRIT detects this and reports an error.
  • Orphaned expectations — An expectation with no step towards clause that is not marked as "__CAMPAIGN__" will not be evaluated. Every expectation must either be a campaign root or step towards another expectation.
  • Over-deep hierarchies — While WRIT supports arbitrary depth, very deep hierarchies are hard to report on. Two or three levels usually suffice.