Contract `maestro.facebook-lookalike-seed-test.v1` version `1.0.0`.

Reference summary:
Current Meta summary checked 2026-09-30: a lookalike source needs at least 100 people, and Meta generally recommends 1,000–5,000. Raw CRM rows, eligible records and native recognized population evidence are different counts. New lookalikes use ad-set location rather than choosing location during audience creation. Meta describes Populating, Audience not created and Ready. Ready proves platform availability only. Advantage+ lookalike can expand beyond the selected percentage. Location, minimum age and Custom Audience exclusions are controls; included audiences can be suggestions. Customer-list use requires the necessary rights, permissions or lawful basis and current opt-out removal. Hashing does not create permission.

Sources:
- Meta lookalike creation guidance (2026-09-30): https://www.facebook.com/business/help/465262276878947/
- Meta customer-list terms (2026-09-30): https://www.facebook.com/legal/terms/customaudience
- Meta Advantage+ lookalike (2026-09-30): https://www.facebook.com/business/help/1212225059146059/
- Meta audience controls and suggestions (2026-09-30): https://www.facebook.com/business/help/938372127764391/

Use only supplied facts. Preserve source facts exactly. Never invent customer permission, owners, source composition, counts, native state, controls, delivery or outcomes. Discard imported `derived` values and recompute them. Unknown, No, numeric Zero and valid Not applicable are distinct. A filename or receipt label containing a word like unknown is not itself unresolved; evaluate the field value. A changed source type invalidates incompatible native evidence.

Decision and request contract:
- Decision precedence is `hold > plan_ready_native_unknown > ready_for_operator_review > delivery_observed_outcomes_pending > inconclusive > stop > continue_test > promote_challenger`.
- Three or more incomplete planning groups triggers only PLAN-* questions, plus REQ-CONTRACT when needed.
- Fewer than three incomplete groups triggers exact evidence requests in contract, source rules, permission, native, controls, test, measurement and outcomes order.
- A missing area owner first falls back to the supplied business owner. If neither is supplied, the owner fallback is exactly `Owner assignment required`.
- Stable requests and their exact text are in the embedded contract JSON.
- The launch boundary is: No decision from this artifact authorizes upload, audience creation, campaign creation, spend, launch or a business-performance claim. An authorized operator must verify the account and act.

Readable Markdown grammar, in exact order:
1. H1 `Facebook lookalike seed and bounded-test handoff`.
2. Record and decision: contract ID, version, record ID, fixture status, decision, input SHA-256.
3. Business and outcome: business, offer, buyer scope, business owner, modeled outcome.
4. Source population: every canonical source field, including raw and eligible counts.
5. Permission: every canonical permission field.
6. Native Meta state: every canonical native field.
7. Delivery controls: every canonical controls field.
8. Bounded test: every canonical test field.
9. Measurement: every canonical measurement field.
10. Decision reasons: one bullet per exact derived reason.
11. Evidence requests: `**ID** — owner: exact request Reason: exact reason`, or the no-requests sentence.
12. Next action: exact derived next action.

Render missing/null as `Unknown`, numeric zero as `0`, false as `False`, and preserve literal state values. Escape literal `|` as `\|`. Preserve every non-derived JSON field and value; recompute and append `derived` with keys in this order: decision, reasons, requests, next_action, input_sha256. Request keys are id, owner, reason, request. Hash the UTF-8 compact JSON of the non-derived record with recursively sorted object keys and unescaped Unicode.

Embedded contract JSON:
```json
{
  "contract_id": "maestro.facebook-lookalike-seed-test.v1",
  "contract_version": "1.0.0",
  "authoritative_input_rule": "Only fields outside derived are source facts. Discard imported derived values and recompute them.",
  "unknown_semantics": "Unknown means evidence is absent. No means an observed negative. Zero means an observed numeric zero. Not applicable is allowed only where the selected state makes a field irrelevant.",
  "decision_precedence": [
    "hold",
    "plan_ready_native_unknown",
    "ready_for_operator_review",
    "delivery_observed_outcomes_pending",
    "inconclusive",
    "stop",
    "continue_test",
    "promote_challenger"
  ],
  "minimum_source_people": 100,
  "recommended_source_range": [
    1000,
    5000
  ],
  "source_types": [
    "customer_list",
    "website",
    "app",
    "page_engagement",
    "video_engagement",
    "lead_form",
    "other_custom_audience"
  ],
  "native_statuses": [
    "unknown",
    "populating",
    "audience_not_created",
    "ready"
  ],
  "permission_states": [
    "unknown",
    "approved",
    "denied",
    "not_applicable"
  ],
  "stable_requests": {
    "REQ-CONTRACT": "Use contract maestro.facebook-lookalike-seed-test.v1 version 1.0.0 and migrate the record before review.",
    "PLAN-SCOPE": "Supply the business, offer, buyer scope and modeled outcome.",
    "PLAN-SOURCE": "Supply the source type, product, geography, outcome window and export cutoff.",
    "PLAN-OWNERS": "Assign owners for the source, permission, native receipt, controls, test and measurement.",
    "PLAN-TEST": "Supply the hypothesis, comparator, fixed variables, cap, maturation rule and decision rules.",
    "REQ-SOURCE-RULES": "Supply the inclusion, exclusion and deduplication rules with raw and eligible counts.",
    "REQ-PERMISSION": "Supply explicit approved-use evidence, owner and opt-out removal process for the customer data.",
    "REQ-NATIVE": "Supply a dated Meta source/lookalike status receipt, source type, selected size and recognized population evidence when exposed.",
    "REQ-CONTROLS": "Supply a dated ad-set receipt for suggestion/control mode, expansion, location, minimum age and customer exclusion.",
    "REQ-TEST": "Supply the bounded test hypothesis, comparator, fixed variables, spend cap, owner, review date and launch condition.",
    "REQ-MEASUREMENT": "Supply the downstream event, CRM join key, outcome owner, maturation rule and decision thresholds.",
    "REQ-OUTCOMES": "Wait for the named outcome window to mature, then read back both arms using the supplied CRM key."
  },
  "broadly_missing_rule": {
    "threshold": 3,
    "groups": {
      "scope": [
        "business.name",
        "business.offer",
        "business.buyer_scope",
        "modeled_outcome"
      ],
      "source": [
        "source.source_type",
        "source.product",
        "source.geography",
        "source.outcome_window",
        "source.export_cutoff"
      ],
      "owners": [
        "source.owner",
        "permission.owner",
        "native.owner",
        "controls.owner",
        "test.owner",
        "measurement.owner"
      ],
      "test": [
        "test.hypothesis",
        "test.comparator",
        "test.spend_cap_per_arm",
        "test.review_date",
        "test.launch_condition"
      ]
    }
  },
  "required_source_fields": [
    "source.source_type",
    "source.product",
    "source.geography",
    "source.outcome_window",
    "source.export_cutoff",
    "source.inclusion_rule",
    "source.exclusion_rules",
    "source.coherence_state",
    "source.deduplication_unit",
    "source.deduplication_key",
    "source.raw_count",
    "source.eligible_count",
    "source.owner",
    "source.evidence_ref"
  ],
  "required_control_fields": [
    "controls.mode",
    "controls.expansion_state",
    "controls.location",
    "controls.minimum_age",
    "controls.customer_exclusion_state",
    "controls.account_objective_context",
    "controls.observed_at",
    "controls.evidence_ref",
    "controls.owner"
  ],
  "required_test_fields": [
    "test.hypothesis",
    "test.comparator",
    "test.fixed_offer",
    "test.fixed_creative",
    "test.fixed_destination",
    "test.fixed_optimization",
    "test.spend_cap_per_arm",
    "test.owner",
    "test.launch_condition",
    "test.review_date"
  ],
  "required_measurement_fields": [
    "measurement.maturation_rule",
    "measurement.downstream_event",
    "measurement.crm_key",
    "measurement.owner",
    "measurement.minimum_mature_events_per_arm",
    "measurement.acceptable_cost_per_outcome"
  ],
  "launch_boundary": "No decision from this artifact authorizes upload, audience creation, campaign creation, spend, launch or a business-performance claim. An authorized operator must verify the account and act.",
  "sources": [
    {
      "label": "Meta lookalike creation guidance",
      "url": "https://www.facebook.com/business/help/465262276878947/",
      "checked": "2026-09-30"
    },
    {
      "label": "Meta customer-list terms",
      "url": "https://www.facebook.com/legal/terms/customaudience",
      "checked": "2026-09-30"
    },
    {
      "label": "Meta Advantage+ lookalike",
      "url": "https://www.facebook.com/business/help/1212225059146059/",
      "checked": "2026-09-30"
    },
    {
      "label": "Meta audience controls and suggestions",
      "url": "https://www.facebook.com/business/help/938372127764391/",
      "checked": "2026-09-30"
    }
  ]
}
```

Canonical blank shape:
```json
{
  "contract_id": "maestro.facebook-lookalike-seed-test.v1",
  "contract_version": "1.0.0",
  "record_id": null,
  "fixture_status": "blank",
  "business": {
    "name": null,
    "offer": null,
    "buyer_scope": null,
    "owner": null
  },
  "modeled_outcome": null,
  "source": {
    "source_type": null,
    "product": null,
    "geography": null,
    "outcome_window": null,
    "export_cutoff": null,
    "inclusion_rule": null,
    "exclusion_rules": [],
    "coherence_state": "unknown",
    "deduplication_unit": null,
    "deduplication_key": null,
    "raw_count": null,
    "eligible_count": null,
    "owner": null,
    "evidence_ref": null
  },
  "permission": {
    "state": "unknown",
    "lawful_use_evidence_ref": null,
    "owner": null,
    "opt_out_removal_process": null,
    "observed_at": null
  },
  "native": {
    "source_type": null,
    "source_alias": null,
    "status": "unknown",
    "observed_at": null,
    "evidence_ref": null,
    "recognized_population_state": "unknown",
    "recognized_population_count": null,
    "lookalike_size_percent": null,
    "owner": null
  },
  "controls": {
    "mode": "unknown",
    "expansion_state": "unknown",
    "location": null,
    "minimum_age": null,
    "customer_exclusion_state": "unknown",
    "account_objective_context": null,
    "observed_at": null,
    "evidence_ref": null,
    "owner": null
  },
  "test": {
    "hypothesis": null,
    "comparator": null,
    "fixed_offer": null,
    "fixed_creative": null,
    "fixed_destination": null,
    "fixed_optimization": null,
    "spend_cap_per_arm": null,
    "owner": null,
    "launch_condition": null,
    "review_date": null
  },
  "measurement": {
    "maturation_rule": null,
    "downstream_event": null,
    "crm_key": null,
    "owner": null,
    "minimum_mature_events_per_arm": null,
    "acceptable_cost_per_outcome": null,
    "delivery_state": "unknown",
    "outcomes_mature_state": "unknown",
    "challenger_spend": null,
    "challenger_mature_outcomes": null,
    "comparator_spend": null,
    "comparator_mature_outcomes": null,
    "pending_outcomes": null,
    "revenue_state": "unknown",
    "evidence_ref": null
  }
}
```

Normative evaluator implementation follows. It defines every decision predicate, exact reason string, request order, owner fallback, next-action string, deduplication rule and hash algorithm. Use it without summarizing or improvising. If code execution is available, save the embedded contract JSON as `contract.json` beside the extracted `evaluate.py`, then run `python3 evaluate.py RECORD.json`. These two embedded blocks are the complete public runtime; no private file is required.

```python
#!/usr/bin/env python3
"""Deterministic evaluator for the Facebook Lookalike seed-and-test contract."""
from __future__ import annotations

import copy
import hashlib
import json
import re
import sys
from datetime import datetime
from pathlib import Path

HERE = Path(__file__).resolve().parent
CONTRACT = json.loads((HERE / "contract.json").read_text())
BAD = re.compile(r"^(?:unknown|not applicable|n/?a|na|pending|unavailable|denied|none|null|tbd|to be determined|planned|requested|expected|placeholder)$", re.I)
NEGATIVE_REF = re.compile(r"\b(?:denied|pending|planned|requested|expected|unavailable|not\s+approved|not\s+available|not\s+complete|will\s+be|to\s+be\s+(?:added|provided|confirmed|verified))\b", re.I)
PLAN_IDS = {"scope":"PLAN-SCOPE", "source":"PLAN-SOURCE", "owners":"PLAN-OWNERS", "test":"PLAN-TEST"}

def get(obj, path):
    cur = obj
    for part in path.split("."):
        if not isinstance(cur, dict): return None
        cur = cur.get(part)
    return cur

def present(value):
    if value is None or value is False: return False
    if isinstance(value, str): return bool(value.strip()) and not BAD.fullmatch(value.strip())
    return True

def affirmative_ref(value):
    return present(value) and not (isinstance(value, str) and NEGATIVE_REF.search(value))

def valid_time(value):
    if not isinstance(value, str): return False
    try:
        parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
        return parsed.tzinfo is not None
    except ValueError:
        return False

def integer(value): return isinstance(value, int) and not isinstance(value, bool)

def add(requests, request_id, owner, reason):
    if any(item["id"] == request_id for item in requests): return
    requests.append({"id":request_id, "owner":owner or "Owner assignment required", "reason":reason, "request":CONTRACT["stable_requests"][request_id]})

def finish(record, requests, decision=None, reasons=None):
    if decision is None: decision = "hold" if requests else "ready_for_operator_review"
    if reasons is None:
        reasons = [r["reason"] for r in requests] if requests else ["The source, permission, native state, controls, bounded test and measurement handoff are complete."]
    clean = json.dumps(record, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()
    if decision == "ready_for_operator_review": next_action = "An authorized operator verifies the account-specific objects and decides whether to launch the bounded test."
    elif decision == "plan_ready_native_unknown": next_action = CONTRACT["stable_requests"]["REQ-NATIVE"]
    elif decision == "delivery_observed_outcomes_pending": next_action = CONTRACT["stable_requests"]["REQ-OUTCOMES"]
    elif decision == "inconclusive": next_action = "Keep the result Inconclusive and follow the supplied cap and maturation rules; do not invent a winner."
    elif decision == "promote_challenger": next_action = "An authorized operator may review the mature evidence and decide whether to promote the challenger."
    elif decision == "stop": next_action = "An authorized operator reviews the mature evidence and stops or redesigns the challenger before more spend."
    elif decision == "continue_test": next_action = "Continue only within the supplied cap until the next valid review trigger."
    else: next_action = requests[0]["request"] if requests else "Resolve the decision reasons before proceeding."
    record["derived"] = {"decision":decision, "reasons":reasons, "requests":requests, "next_action":next_action, "input_sha256":hashlib.sha256(clean).hexdigest()}
    return record

def evaluate(source):
    x = copy.deepcopy(source); x.pop("derived", None); requests = []
    if x.get("contract_id") != CONTRACT["contract_id"] or x.get("contract_version") != CONTRACT["contract_version"]:
        add(requests, "REQ-CONTRACT", get(x,"business.owner"), "The contract identity or version is incompatible.")

    gaps = {name:[path for path in paths if not present(get(x,path))] for name,paths in CONTRACT["broadly_missing_rule"]["groups"].items()}
    incomplete = [name for name,missing in gaps.items() if missing]
    if len(incomplete) >= CONTRACT["broadly_missing_rule"]["threshold"]:
        for name in ("scope","source","owners","test"):
            if name in incomplete: add(requests, PLAN_IDS[name], get(x,"business.owner"), f'The {name} planning group is incomplete: {", ".join(gaps[name])}.')
        return finish(x, requests)

    missing_source = [path for path in CONTRACT["required_source_fields"] if not (affirmative_ref(get(x,path)) if path.endswith("evidence_ref") else present(get(x,path)))]
    source_type = get(x,"source.source_type")
    raw, eligible = get(x,"source.raw_count"), get(x,"source.eligible_count")
    source_bad = bool(missing_source)
    source_reasons = []
    if missing_source: source_reasons.append(f'Missing or unresolved source fields: {", ".join(missing_source)}.')
    if source_type not in CONTRACT["source_types"]: source_reasons.append("The source type is unsupported.")
    if get(x,"source.coherence_state") != "coherent": source_reasons.append("The source does not represent one documented modeled outcome.")
    if not integer(raw) or not integer(eligible) or raw < 0 or eligible < 0 or eligible > raw: source_reasons.append("Raw and eligible counts must be non-negative integers, with eligible no greater than raw.")
    elif eligible < CONTRACT["minimum_source_people"]: source_reasons.append(f'Eligible source count {eligible} is below Meta\'s currently stated minimum of {CONTRACT["minimum_source_people"]} people.')
    if not valid_time(get(x,"source.export_cutoff")): source_reasons.append("The export cutoff is not a calendar-valid timezone-aware ISO 8601 timestamp.")
    if source_bad or source_reasons: add(requests,"REQ-SOURCE-RULES",get(x,"source.owner") or get(x,"business.owner")," ".join(source_reasons) or "The source rules are incomplete.")

    permission = get(x,"permission.state")
    permission_ok = permission == "approved" and affirmative_ref(get(x,"permission.lawful_use_evidence_ref")) and present(get(x,"permission.owner")) and present(get(x,"permission.opt_out_removal_process")) and valid_time(get(x,"permission.observed_at"))
    if source_type != "customer_list" and permission == "not_applicable": permission_ok = present(get(x,"permission.owner")) and valid_time(get(x,"permission.observed_at"))
    if not permission_ok: add(requests,"REQ-PERMISSION",get(x,"permission.owner") or get(x,"business.owner"),"Explicit current approved-use evidence, owner, observation time or opt-out removal process is incomplete.")

    native_status = get(x,"native.status")
    native_type = get(x,"native.source_type")
    native_owner = get(x,"native.owner") or get(x,"business.owner")
    if native_status == "unknown":
        add(requests,"REQ-NATIVE",native_owner,"The native source/lookalike state is Unknown because no dated account receipt was supplied.")
    elif native_status != "ready":
        add(requests,"REQ-NATIVE",native_owner,f'The observed native status is {native_status}; it is not Ready.')
    else:
        native_reasons=[]
        if native_type != source_type: native_reasons.append("The native receipt source type does not match the planned source type.")
        if not valid_time(get(x,"native.observed_at")): native_reasons.append("The native observation time is invalid or missing.")
        if not affirmative_ref(get(x,"native.evidence_ref")): native_reasons.append("The native evidence reference is missing or non-affirmative.")
        if not present(get(x,"native.source_alias")) or not present(native_owner): native_reasons.append("The native source alias or owner is missing.")
        pct=get(x,"native.lookalike_size_percent")
        if not isinstance(pct,(int,float)) or isinstance(pct,bool) or pct <= 0: native_reasons.append("The selected lookalike percentage is missing or invalid.")
        state,count=get(x,"native.recognized_population_state"),get(x,"native.recognized_population_count")
        if state == "observed" and (not integer(count) or count < 0): native_reasons.append("Observed recognized-population state requires a non-negative integer count.")
        if state == "zero" and count != 0: native_reasons.append("Zero recognized-population state requires count 0.")
        if native_reasons: add(requests,"REQ-NATIVE",native_owner," ".join(native_reasons))

    control_missing=[p for p in CONTRACT["required_control_fields"] if not (affirmative_ref(get(x,p)) if p.endswith("evidence_ref") else present(get(x,p)))]
    control_reasons=[]
    if control_missing: control_reasons.append(f'Missing or unresolved control fields: {", ".join(control_missing)}.')
    if get(x,"controls.mode") not in ("suggestion","control"): control_reasons.append("Suggestion-versus-control mode is unresolved.")
    if get(x,"controls.customer_exclusion_state") != "yes": control_reasons.append("A current customer exclusion is not affirmatively observed.")
    if not valid_time(get(x,"controls.observed_at")): control_reasons.append("The control observation time is invalid or missing.")
    if control_reasons: add(requests,"REQ-CONTROLS",get(x,"controls.owner") or get(x,"business.owner")," ".join(control_reasons))

    test_missing=[p for p in CONTRACT["required_test_fields"] if not present(get(x,p))]
    cap=get(x,"test.spend_cap_per_arm")
    if test_missing or not isinstance(cap,(int,float)) or isinstance(cap,bool) or cap <= 0:
        reason=(f'Missing or unresolved test fields: {", ".join(test_missing)}. ' if test_missing else "") + "Spend cap per arm must be a positive number."
        add(requests,"REQ-TEST",get(x,"test.owner") or get(x,"business.owner"),reason)

    measure_missing=[p for p in CONTRACT["required_measurement_fields"] if not present(get(x,p))]
    min_events=get(x,"measurement.minimum_mature_events_per_arm"); acceptable=get(x,"measurement.acceptable_cost_per_outcome")
    if measure_missing or not integer(min_events) or min_events <= 0 or not isinstance(acceptable,(int,float)) or isinstance(acceptable,bool) or acceptable <= 0:
        add(requests,"REQ-MEASUREMENT",get(x,"measurement.owner") or get(x,"business.owner"),f'Missing, unresolved or invalid measurement fields: {", ".join(measure_missing) if measure_missing else "decision thresholds"}.')

    non_native=[r for r in requests if r["id"] not in ("REQ-NATIVE",)]
    if not non_native and native_status == "unknown":
        return finish(x,requests,"plan_ready_native_unknown",["The source plan, permission, controls, test and measurement fields are complete, but native Meta state remains Unknown."])
    if requests: return finish(x,requests)

    delivery=get(x,"measurement.delivery_state"); mature=get(x,"measurement.outcomes_mature_state")
    if delivery != "yes": return finish(x,[],"ready_for_operator_review")
    if mature != "yes":
        add(requests,"REQ-OUTCOMES",get(x,"measurement.owner"),"Delivery is observed, but the named downstream outcomes have not matured.")
        return finish(x,requests,"delivery_observed_outcomes_pending",["Delivery is observed, but the supplied outcome window is still pending."])
    values={k:get(x,f"measurement.{k}") for k in ("challenger_spend","challenger_mature_outcomes","comparator_spend","comparator_mature_outcomes")}
    if not all(isinstance(v,(int,float)) and not isinstance(v,bool) and v >= 0 for v in values.values()) or not affirmative_ref(get(x,"measurement.evidence_ref")):
        add(requests,"REQ-MEASUREMENT",get(x,"measurement.owner"),"Mature arm spend, outcome counts or the outcome evidence reference is missing or invalid.")
        return finish(x,requests)
    c_events=values["challenger_mature_outcomes"]; b_events=values["comparator_mature_outcomes"]
    if c_events < min_events or b_events < min_events:
        return finish(x,[],"inconclusive",[f'The mature sample is below the supplied minimum of {min_events} outcomes per arm.'])
    c_cost=values["challenger_spend"]/c_events if c_events else float("inf"); b_cost=values["comparator_spend"]/b_events if b_events else float("inf")
    if c_cost <= acceptable and c_cost < b_cost: return finish(x,[],"promote_challenger",["The challenger beat the comparator on the supplied mature cost rule and stayed within the supplied acceptable cost."])
    if c_cost > acceptable and b_cost <= acceptable: return finish(x,[],"stop",["The challenger missed the supplied acceptable cost while the comparator met it."])
    return finish(x,[],"continue_test",["The mature result does not satisfy the supplied promote or stop rule."])

if __name__ == "__main__":
    source=json.load(open(sys.argv[1])) if len(sys.argv)>1 else json.load(sys.stdin)
    print(json.dumps(evaluate(source),indent=2,ensure_ascii=False))

```

Normative readable Markdown renderer and browser-parity implementation follows. Its `readableMarkdown` function is the complete rendering template, including the exact no-requests sentence.

```javascript
import {createHash} from 'node:crypto';

const BAD=/^(?:unknown|not applicable|n\/?a|na|pending|unavailable|denied|none|null|tbd|to be determined|planned|requested|expected|placeholder)$/i;
const NEGATIVE=/\b(?:denied|pending|planned|requested|expected|unavailable|not\s+approved|not\s+available|not\s+complete|will\s+be|to\s+be\s+(?:added|provided|confirmed|verified))\b/i;
const PLAN_IDS={scope:'PLAN-SCOPE',source:'PLAN-SOURCE',owners:'PLAN-OWNERS',test:'PLAN-TEST'};
const clone=x=>JSON.parse(JSON.stringify(x));
const get=(obj,path)=>path.split('.').reduce((v,k)=>v&&typeof v==='object'?v[k]:undefined,obj);
const present=v=>v!==null&&v!==undefined&&v!==false&&(!(typeof v==='string')||(v.trim()&&!BAD.test(v.trim())));
const affirmative=v=>present(v)&&!(typeof v==='string'&&NEGATIVE.test(v));
const integer=v=>Number.isInteger(v);
const validTime=v=>{if(typeof v!=='string')return false;const m=v.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(Z|([+-])(\d{2}):(\d{2}))$/);if(!m)return false;const d=new Date(v);if(Number.isNaN(d.valueOf()))return false;const oh=Number(m[10]||0),om=Number(m[11]||0);if(oh>23||om>59)return false;const signed=m[8]==='Z'?0:(m[9]==='+'?1:-1)*(oh*60+om),local=new Date(d.getTime()+signed*60000);return local.getUTCFullYear()===Number(m[1])&&local.getUTCMonth()+1===Number(m[2])&&local.getUTCDate()===Number(m[3])&&local.getUTCHours()===Number(m[4])&&local.getUTCMinutes()===Number(m[5])&&local.getUTCSeconds()===Number(m[6])};
const stable=o=>{if(Array.isArray(o))return `[${o.map(stable).join(',')}]`;if(o&&typeof o==='object')return `{${Object.keys(o).sort().map(k=>`${JSON.stringify(k)}:${stable(o[k])}`).join(',')}}`;return JSON.stringify(o)};
const hash=o=>createHash('sha256').update(stable(o)).digest('hex');
const add=(rs,c,id,owner,reason)=>{if(!rs.some(x=>x.id===id))rs.push({id,owner:owner||'Owner assignment required',reason,request:c.stable_requests[id]})};
function finish(x,requests,decision=null,reasons=null){decision??=requests.length?'hold':'ready_for_operator_review';reasons??=requests.length?requests.map(x=>x.reason):['The source, permission, native state, controls, bounded test and measurement handoff are complete.'];let next_action;if(decision==='ready_for_operator_review')next_action='An authorized operator verifies the account-specific objects and decides whether to launch the bounded test.';else if(decision==='plan_ready_native_unknown')next_action='Supply a dated Meta source/lookalike status receipt, source type, selected size and recognized population evidence when exposed.';else if(decision==='delivery_observed_outcomes_pending')next_action='Wait for the named outcome window to mature, then read back both arms using the supplied CRM key.';else if(decision==='inconclusive')next_action='Keep the result Inconclusive and follow the supplied cap and maturation rules; do not invent a winner.';else if(decision==='promote_challenger')next_action='An authorized operator may review the mature evidence and decide whether to promote the challenger.';else if(decision==='stop')next_action='An authorized operator reviews the mature evidence and stops or redesigns the challenger before more spend.';else if(decision==='continue_test')next_action='Continue only within the supplied cap until the next valid review trigger.';else next_action=requests[0]?.request||'Resolve the decision reasons before proceeding.';x.derived={decision,reasons,requests,next_action,input_sha256:hash(x)};return x}

export function evaluate(source,c){const x=clone(source);delete x.derived;const requests=[];
  if(x.contract_id!==c.contract_id||x.contract_version!==c.contract_version)add(requests,c,'REQ-CONTRACT',get(x,'business.owner'),'The contract identity or version is incompatible.');
  const gaps=Object.fromEntries(Object.entries(c.broadly_missing_rule.groups).map(([n,ps])=>[n,ps.filter(p=>!present(get(x,p)))])),incomplete=Object.keys(gaps).filter(n=>gaps[n].length);
  if(incomplete.length>=c.broadly_missing_rule.threshold){for(const name of ['scope','source','owners','test'])if(incomplete.includes(name))add(requests,c,PLAN_IDS[name],get(x,'business.owner'),`The ${name} planning group is incomplete: ${gaps[name].join(', ')}.`);return finish(x,requests)}
  const missingSource=c.required_source_fields.filter(p=>!(p.endsWith('evidence_ref')?affirmative(get(x,p)):present(get(x,p)))),sourceType=get(x,'source.source_type'),raw=get(x,'source.raw_count'),eligible=get(x,'source.eligible_count'),sr=[];
  if(missingSource.length)sr.push(`Missing or unresolved source fields: ${missingSource.join(', ')}.`);if(!c.source_types.includes(sourceType))sr.push('The source type is unsupported.');if(get(x,'source.coherence_state')!=='coherent')sr.push('The source does not represent one documented modeled outcome.');if(!integer(raw)||!integer(eligible)||raw<0||eligible<0||eligible>raw)sr.push('Raw and eligible counts must be non-negative integers, with eligible no greater than raw.');else if(eligible<c.minimum_source_people)sr.push(`Eligible source count ${eligible} is below Meta's currently stated minimum of ${c.minimum_source_people} people.`);if(!validTime(get(x,'source.export_cutoff')))sr.push('The export cutoff is not a calendar-valid timezone-aware ISO 8601 timestamp.');if(sr.length)add(requests,c,'REQ-SOURCE-RULES',get(x,'source.owner')||get(x,'business.owner'),sr.join(' '));
  const ps=get(x,'permission.state');let pok=ps==='approved'&&affirmative(get(x,'permission.lawful_use_evidence_ref'))&&present(get(x,'permission.owner'))&&present(get(x,'permission.opt_out_removal_process'))&&validTime(get(x,'permission.observed_at'));if(sourceType!=='customer_list'&&ps==='not_applicable')pok=present(get(x,'permission.owner'))&&validTime(get(x,'permission.observed_at'));if(!pok)add(requests,c,'REQ-PERMISSION',get(x,'permission.owner')||get(x,'business.owner'),'Explicit current approved-use evidence, owner, observation time or opt-out removal process is incomplete.');
  const ns=get(x,'native.status'),nt=get(x,'native.source_type'),no=get(x,'native.owner')||get(x,'business.owner');if(ns==='unknown')add(requests,c,'REQ-NATIVE',no,'The native source/lookalike state is Unknown because no dated account receipt was supplied.');else if(ns!=='ready')add(requests,c,'REQ-NATIVE',no,`The observed native status is ${ns}; it is not Ready.`);else{const nr=[];if(nt!==sourceType)nr.push('The native receipt source type does not match the planned source type.');if(!validTime(get(x,'native.observed_at')))nr.push('The native observation time is invalid or missing.');if(!affirmative(get(x,'native.evidence_ref')))nr.push('The native evidence reference is missing or non-affirmative.');if(!present(get(x,'native.source_alias'))||!present(no))nr.push('The native source alias or owner is missing.');const pct=get(x,'native.lookalike_size_percent');if(typeof pct!=='number'||pct<=0)nr.push('The selected lookalike percentage is missing or invalid.');const state=get(x,'native.recognized_population_state'),count=get(x,'native.recognized_population_count');if(state==='observed'&&(!integer(count)||count<0))nr.push('Observed recognized-population state requires a non-negative integer count.');if(state==='zero'&&count!==0)nr.push('Zero recognized-population state requires count 0.');if(nr.length)add(requests,c,'REQ-NATIVE',no,nr.join(' '))}
  const cm=c.required_control_fields.filter(p=>!(p.endsWith('evidence_ref')?affirmative(get(x,p)):present(get(x,p)))),cr=[];if(cm.length)cr.push(`Missing or unresolved control fields: ${cm.join(', ')}.`);if(!['suggestion','control'].includes(get(x,'controls.mode')))cr.push('Suggestion-versus-control mode is unresolved.');if(get(x,'controls.customer_exclusion_state')!=='yes')cr.push('A current customer exclusion is not affirmatively observed.');if(!validTime(get(x,'controls.observed_at')))cr.push('The control observation time is invalid or missing.');if(cr.length)add(requests,c,'REQ-CONTROLS',get(x,'controls.owner')||get(x,'business.owner'),cr.join(' '));
  const tm=c.required_test_fields.filter(p=>!present(get(x,p))),cap=get(x,'test.spend_cap_per_arm');if(tm.length||typeof cap!=='number'||cap<=0)add(requests,c,'REQ-TEST',get(x,'test.owner')||get(x,'business.owner'),`${tm.length?`Missing or unresolved test fields: ${tm.join(', ')}. `:''}Spend cap per arm must be a positive number.`);
  const mm=c.required_measurement_fields.filter(p=>!present(get(x,p))),me=get(x,'measurement.minimum_mature_events_per_arm'),ac=get(x,'measurement.acceptable_cost_per_outcome');if(mm.length||!integer(me)||me<=0||typeof ac!=='number'||ac<=0)add(requests,c,'REQ-MEASUREMENT',get(x,'measurement.owner')||get(x,'business.owner'),`Missing, unresolved or invalid measurement fields: ${mm.length?mm.join(', '):'decision thresholds'}.`);
  if(!requests.some(r=>r.id!=='REQ-NATIVE')&&ns==='unknown')return finish(x,requests,'plan_ready_native_unknown',['The source plan, permission, controls, test and measurement fields are complete, but native Meta state remains Unknown.']);if(requests.length)return finish(x,requests);
  const delivery=get(x,'measurement.delivery_state'),mature=get(x,'measurement.outcomes_mature_state');if(delivery!=='yes')return finish(x,[],'ready_for_operator_review');if(mature!=='yes'){add(requests,c,'REQ-OUTCOMES',get(x,'measurement.owner'),'Delivery is observed, but the named downstream outcomes have not matured.');return finish(x,requests,'delivery_observed_outcomes_pending',['Delivery is observed, but the supplied outcome window is still pending.'])}
  const vals=Object.fromEntries(['challenger_spend','challenger_mature_outcomes','comparator_spend','comparator_mature_outcomes'].map(k=>[k,get(x,`measurement.${k}`)]));if(!Object.values(vals).every(v=>typeof v==='number'&&v>=0)||!affirmative(get(x,'measurement.evidence_ref'))){add(requests,c,'REQ-MEASUREMENT',get(x,'measurement.owner'),'Mature arm spend, outcome counts or the outcome evidence reference is missing or invalid.');return finish(x,requests)}if(vals.challenger_mature_outcomes<me||vals.comparator_mature_outcomes<me)return finish(x,[],'inconclusive',[`The mature sample is below the supplied minimum of ${me} outcomes per arm.`]);const cc=vals.challenger_mature_outcomes?vals.challenger_spend/vals.challenger_mature_outcomes:Infinity,bc=vals.comparator_mature_outcomes?vals.comparator_spend/vals.comparator_mature_outcomes:Infinity;if(cc<=ac&&cc<bc)return finish(x,[],'promote_challenger',['The challenger beat the comparator on the supplied mature cost rule and stayed within the supplied acceptable cost.']);if(cc>ac&&bc<=ac)return finish(x,[],'stop',['The challenger missed the supplied acceptable cost while the comparator met it.']);return finish(x,[],'continue_test',['The mature result does not satisfy the supplied promote or stop rule.'])}

const md=v=>v===null||v===undefined?'Unknown':typeof v==='boolean'?(v?'True':'False'):String(v).replaceAll('|','\\|');
const section=(obj,labels)=>Object.entries(labels).map(([k,l])=>`- **${l}:** ${md(obj?.[k])}`).join('\n');
export function readableMarkdown(r){const labels={source:{source_type:'Source type',product:'Product',geography:'Geography',outcome_window:'Outcome window',export_cutoff:'Export cutoff',inclusion_rule:'Inclusion rule',exclusion_rules:'Exclusion rules',coherence_state:'Coherence state',deduplication_unit:'Deduplication unit',deduplication_key:'Deduplication key',raw_count:'Raw count',eligible_count:'Eligible count',owner:'Source owner',evidence_ref:'Evidence reference'},permission:{state:'State',lawful_use_evidence_ref:'Lawful-use evidence reference',owner:'Permission owner',opt_out_removal_process:'Opt-out removal process',observed_at:'Observed at'},native:{source_type:'Source type',source_alias:'Source alias',status:'Status',observed_at:'Observed at',evidence_ref:'Evidence reference',recognized_population_state:'Recognized population state',recognized_population_count:'Recognized population count',lookalike_size_percent:'Lookalike size percent',owner:'Native receipt owner'},controls:{mode:'Mode',expansion_state:'Expansion state',location:'Location',minimum_age:'Minimum age',customer_exclusion_state:'Customer exclusion state',account_objective_context:'Account and objective context',observed_at:'Observed at',evidence_ref:'Evidence reference',owner:'Controls owner'},test:{hypothesis:'Hypothesis',comparator:'Comparator',fixed_offer:'Fixed offer',fixed_creative:'Fixed creative',fixed_destination:'Fixed destination',fixed_optimization:'Fixed optimization',spend_cap_per_arm:'Spend cap per arm',owner:'Test owner',launch_condition:'Launch condition',review_date:'Review date'},measurement:{maturation_rule:'Maturation rule',downstream_event:'Downstream event',crm_key:'CRM key',owner:'Measurement owner',minimum_mature_events_per_arm:'Minimum mature events per arm',acceptable_cost_per_outcome:'Acceptable cost per outcome',delivery_state:'Delivery state',outcomes_mature_state:'Outcomes mature state',challenger_spend:'Challenger spend',challenger_mature_outcomes:'Challenger mature outcomes',comparator_spend:'Comparator spend',comparator_mature_outcomes:'Comparator mature outcomes',pending_outcomes:'Pending outcomes',revenue_state:'Revenue state',evidence_ref:'Evidence reference'}};const req=r.derived?.requests||[],reasons=r.derived?.reasons||[];return `# Facebook lookalike seed and bounded-test handoff\n\n## Record and decision\n\n- **Contract ID:** ${md(r.contract_id)}\n- **Contract version:** ${md(r.contract_version)}\n- **Record ID:** ${md(r.record_id)}\n- **Fixture status:** ${md(r.fixture_status)}\n- **Decision:** ${md(r.derived?.decision)}\n- **Input SHA-256:** ${md(r.derived?.input_sha256)}\n\n## Business and outcome\n\n- **Business:** ${md(r.business?.name)}\n- **Offer:** ${md(r.business?.offer)}\n- **Buyer scope:** ${md(r.business?.buyer_scope)}\n- **Business owner:** ${md(r.business?.owner)}\n- **Modeled outcome:** ${md(r.modeled_outcome)}\n\n## Source population\n\n${section(r.source,labels.source)}\n\n## Permission\n\n${section(r.permission,labels.permission)}\n\n## Native Meta state\n\n${section(r.native,labels.native)}\n\n## Delivery controls\n\n${section(r.controls,labels.controls)}\n\n## Bounded test\n\n${section(r.test,labels.test)}\n\n## Measurement\n\n${section(r.measurement,labels.measurement)}\n\n## Decision reasons\n\n${reasons.length?reasons.map(x=>`- ${md(x)}`).join('\n'):'- Unknown'}\n\n## Evidence requests\n\n${req.length?req.map(x=>`- **${md(x.id)}** — ${md(x.owner)}: ${md(x.request)} Reason: ${md(x.reason)}`).join('\n'):'- No planning evidence requests remain. Account-specific launch checks still apply.'}\n\n## Next action\n\n${md(r.derived?.next_action)}\n`}

```


Task: build or complete one record from the supplied planning facts and receipts. If three or more planning groups are incomplete, ask only the focused PLAN-* questions and return the partial record. Otherwise evaluate every gate. Do not turn a plan into an account receipt.

Return exactly: (1) decision; (2) readable handoff; (3) exact stable requests or focused PLAN-* questions; (4) canonical JSON in one code fence; (5) one exact next action.

PASTE PLANNING FACTS AND RECEIPTS BELOW:
