# Change proposal. The agent fills this from the read-only pass and stops.
# Any change to targets, values or scope after review means a new version.
proposal_id: "<system>-<yyyymmdd>-<slug>"
version: 1
created_at: "<YYYY-MM-DDTHH:MM:SSZ>"
requested_by: "<role>"
request: "<the user's request in one sentence>"
approver_role: "<role that may approve this change class>"
run_mode: "<interactive | scheduled | triggered | headless>"  # non-interactive runs stop here and queue the proposal

connection:
  system: "<Meta Ads | Google Tag Manager | HubSpot | ...>"
  identity: "<role of the identity that will write>"
  read_pass_layer: "<for example ads_read token; GTM Read; sobject-reads>"
  inventory_date: "<YYYY-MM-DD>"

targets:
  target_query: "<the query or filter that selected the targets>"
  target_count: 0  # from a separate count query; must equal the number of rows
  truncated: false  # true if the read hit a row cap or page size

limits:
  max_objects: 10
  max_daily_spend_change: "<currency and amount, or n/a>"
  allow_partial: false
  expires_after_minutes: 30
  first_batch_size: 1

out_of_scope:
  - "<objects the approver excluded>"

rows:
  - row: 1
    account: "<customer ID 123-456-7890 | act_XXXXXXXXX | GTM-XXXXXXX workspace n | portal ID | project ID>"
    object_type: "<campaign | ad_set | tag | contact | property | table>"
    object_id: "<stable ID>"
    object_name: "<readable name>"
    field: "<field name as the API returns it>"
    unit: "<money fields: unit as the API holds it, for example USD cents or micros; else n/a>"
    level: "<money fields: campaign or ad set; shared or single budget; else n/a>"
    covers: []  # shared budgets and portfolio strategies: every campaign affected
    before: "<raw value read at snapshot time>"
    before_readable: "<for money: the amount in currency, for example USD 200.00>"
    before_read_at: "<YYYY-MM-DDTHH:MM:SSZ>"
    version_marker: "<fingerprint, last-modified time, or n/a>"
    after: "<proposed raw value; complete object for full-replacement updates>"
    after_readable: "<for money: the amount in currency>"
    reason: "<why>"
    evidence: "<report link, query or record reference>"
    validated_without_writing: "<quickPreview, --dry-run, validate_only, dry run, EXPLAIN, or n/a>"
    side_effects:
      - "<workflow, validation rule, trigger, learning phase, shared budget, audience, message or sync this touches>"
    hard_to_reverse: false
    reversibility: "<reversible | reversible until YYYY-MM-DD | irreversible>"
    rollback: "<exact action, pre-change clone or snapshot name if any, and who can run it without the agent>"
    rollback_window: "<from the actual setting, not the product default>"
    not_undone: "<what the rollback does not reverse>"
    action_key: "<proposal_id>-v1-1"

totals:
  objects: 0
  daily_spend_change: "<amount or n/a>"
  recipients_reached: 0
  items_deleted: 0

monitoring:
  - signal: "<for example spend pacing on the changed ad sets>"
    trigger: "<numeric threshold stated by the approver>"
    owner: "<role>"
    response: "<what the owner does>"

evidence_gaps:
  - "<missing or stale evidence the reviewer should know about>"

# Filled from the human approver, never by the agent from its own text.
# A stored task prompt, an always-allow client setting or another model's review is not approval.
approval:
  status: "<pending | approved | rejected | expired>"
  approver: "<name or user ID as the team records it>"
  decided_at: "<YYYY-MM-DDTHH:MM:SSZ>"
  approved_version: 1
  conditions: "<for example publish stays with a person>"
  hard_to_reverse_rows_approved: []
  advisory_reviews: []  # model or agent reviews, recorded separately
