{
 "tool": "eclipse-trunk-mcp",
 "version": "0.1.0",
 "bench_date": "2026-10-25",
 "bench_version": "0.1.0",
 "environment": "simulator",
 "qualifier": "simulator",
 "verdict": "CERTIFIED(simulator)",
 "checks": [
  {
   "id": "T1",
   "name": "logs masked beyond last-4 (fixture file path)",
   "verdict": "pass",
   "evidence": "logs_read over the fixture file returns all 6 Twilio-shaped entries with every one of the 7 full fixture numbers absent from the output \u2014 counterparty and enrolled numbers alike appear only masked beyond last-4 (***0132 form present) \u2014 and no message body text is carried in log output at all"
  },
  {
   "id": "T2",
   "name": "masking + body hashing across sends, receipts, and both state files",
   "verdict": "pass",
   "evidence": "after a simulated send to the counterparty number, that full number and the message body text appear in NONE of: the send output, the family receipt view, the trunk state file, or the core state file; the stored send record carries the masked form '+1 ***-***-0132' and the body's SHA-256 (matching the bench-computed hash) with body_chars only \u2014 bodies are hashed, digits are absent, and the consent_record_id rides the record"
  },
  {
   "id": "T3",
   "name": "send surface is *_sim only: denylist refused by direct call",
   "verdict": "pass",
   "evidence": "tools/list is exactly the 8 classified V0 tools; direct calls to send_sms and place_call (the un-suffixed forms), e911_config, e911_address_write, emergency_call_config, caller_id_set/spoof/override, number_release, number_provision, raw_vendor_call and consent_edit are each refused with RPC -32602 \u2014 sends exist ONLY as *_sim through the core gate, and no emergency, caller-ID, provisioning, release, or raw-vendor surface exists at any tier"
  },
  {
   "id": "T4",
   "name": "gated sends: valid path + named refusals",
   "verdict": "pass",
   "evidence": "the valid path writes exactly one new simulated send (SM sid, simulated=true) and one new sms_sent_simulated family receipt carrying the consent_record_id; the three refusal classes each name their gate \u2014 SEND_REFUSED[number:registration_broken] for the suspended-campaign number, SEND_REFUSED[number:number_not_enrolled] for an unenrolled from-number, SEND_REFUSED[consent:no_consent] for a consent-less subject \u2014 and none of them adds a send record; number_search returns fixture inventory while writing no receipts (search provisions nothing)"
  },
  {
   "id": "T5",
   "name": "place_call_sim requires a named human speaker",
   "verdict": "pass",
   "evidence": "place_call_sim without human_speaker is refused with HUMAN_SPEAKER_REQUIRED before the gate is even consulted (family ledger and send records both unchanged by the attempt); naming the human places the simulated call (CA sid) and the record carries the speaker's name \u2014 a represented human call always has a named human on it (spec 8.1)"
  },
  {
   "id": "T6",
   "name": "recording policy is T3: per-number token, every call",
   "verdict": "pass",
   "evidence": "recording-policy changes are T3 on every call: no token -> CONFIRMATION_REQUIRED; another number's token -> CONFIRMATION_REQUIRED (the token is bound to its number); the correct per-number token lands the change with a recording_policy_changed receipt; a SECOND change without the token is refused again \u2014 the token does not carry over between changes \u2014 and re-presenting it lands the second change (2 change receipts total)"
  },
  {
   "id": "T7",
   "name": "stub-HTTP log path == fixture-file path",
   "verdict": "pass",
   "evidence": "the same logs_read call served from the local Twilio-shaped stub over HTTP returns the identical masked log set as the fixture-file path \u2014 same 6 entries, same sids, same masked from/to, same statuses \u2014 with only the source label differing (twilio_stub vs fixture_file): the read path is transport-faithful, and masking holds identically on both"
  },
  {
   "id": "T8",
   "name": "SPEC 5.3 ACCEPTANCE: one revocation refuses two adapter surfaces",
   "verdict": "pass",
   "evidence": "SPEC 5.3 ACCEPTANCE, bench-owned throughout: with consent standing, Trunk sends and the bench-written minimal adapter (no consent state of its own, core_engine against the shared file) both ALLOW; ONE consent_revoke through the core then makes Trunk refuse by name (SEND_REFUSED[consent:consent_revoked]) and the minimal adapter deny at stage=consent/consent_revoked via its check-time call; the ledger grows by exactly the two violation records and nothing else \u2014 zero new sends, zero new authorizations \u2014 and the chain still verifies"
  },
  {
   "id": "T9",
   "name": "receipt chain valid; tamper detected",
   "verdict": "pass",
   "evidence": "receipt_list through the adapter verifies the family chain over the full run; one forged receipt detail in bench state flips chain_valid to false; restoring the bytes restores true \u2014 the adapter's view of the ledger is the core's, not a copy"
  },
  {
   "id": "U1",
   "name": "auth enforced",
   "verdict": "na",
   "evidence": "local stdio transport: no credential layer exists to test; process ownership is the boundary"
  },
  {
   "id": "U2",
   "name": "schema valid",
   "verdict": "pass",
   "evidence": "tools/list returns exactly the 8 adapter tools, each with inputSchema type object + properties + required"
  },
  {
   "id": "U3",
   "name": "network surface is the env-set local stub only",
   "verdict": "pass",
   "evidence": "the adapter's only network call site is a urlopen against the ECLIPSE_TRUNK_STUB_URL environment value (the bench pointed it at 127.0.0.1); no carrier endpoint string (api.twilio.com) and no HTTP-client library exists in the server, and the stub binds loopback only \u2014 V0 reaches no carrier, structurally"
  },
  {
   "id": "U4",
   "name": "no secret leak",
   "verdict": "pass",
   "evidence": "no secret patterns in 41 tool outputs"
  },
  {
   "id": "U5",
   "name": "sane errors",
   "verdict": "pass",
   "evidence": "unenrolled number -> named NUMBER_NOT_ENROLLED error; malformed line survived (receipt_list answers); unknown method -> -32601"
  },
  {
   "id": "U6",
   "name": "process health",
   "verdict": "pass",
   "evidence": "initialize answered in 207 ms; ping ok"
  },
  {
   "id": "U7",
   "name": "audit completeness",
   "verdict": "pass",
   "evidence": "the family ledger covers the adapter run in order (['consent_recorded', 'sms_sent_simulated', 'recording_policy_changed', 'violation'] present as a subsequence); 21 receipts, seq strictly increasing, chain valid \u2014 simulated sends, policy changes, and consent violations all land in the one core-held ledger the adapter reads"
  },
  {
   "id": "U8",
   "name": "bench isolation",
   "verdict": "pass",
   "evidence": "workspace product state byte-identical before/after bench"
  }
 ],
 "notes": [
  "Simulator scope: V0 fixture adapter \u2014 sends are simulated records only, gated through the Voice Core state shared with the bench's own core process; no Twilio account exists and no carrier was contacted. Live Twilio wiring is V1, behind the owner's account and the counsel gate.",
  "Masking scope proven for counterparty (customer) numbers and message bodies across every trunk output, the trunk state file, the family receipts, and the core state file. Registry-identity reads (registration_status for an enrolled number, number_search over carrier inventory) return the number being queried by design \u2014 those are the operator's own numbers and unprovisioned inventory, not counterparty PII.",
  "Number provisioning and release are V1 surfaces and do not exist in V0; number_search provisions nothing (T4 verifies the ledger stays empty on search)."
 ],
 "history": "append-only; prior runs retained in results/"
}