{
 "tool": "eclipse-span-mcp",
 "version": "0.1.0",
 "bench_date": "2026-10-26",
 "bench_version": "0.1.0",
 "environment": "local",
 "qualifier": "local",
 "verdict": "CERTIFIED(local)",
 "checks": [
  {
   "id": "S1",
   "name": "property battery: suite-generated random valid chains depth 1-8 all accepted + recorded; ledger verifies",
   "verdict": "pass",
   "evidence": "8 suite-generated chains of depths [1, 2, 3, 4, 5, 6, 7, 8] (random subsets of caps/allow, random cap 0..parent, exp narrowed 0-6d, tier 0..parent, budgets randomly narrowed with subtree floors) \u2014 every spawn_check PASS + spawn_record recorded (36 spawns, all lineage_substrate span-ledger); every chain head chain_verify VERIFIED with no unchecked flags; ledger_chain_valid true"
  },
  {
   "id": "S2",
   "name": "per-component amplification mutations (Caps, Cap, Allow, Exp, Depth, Tier + budgets max_children/max_subtree/spawn_rate) each refused BY ITS NAME; chain state byte-identical after (refusal receipts excepted)",
   "verdict": "pass",
   "evidence": "9 single-component mutations of a valid child (caps +1, cap 2000->2001, allow +1, exp +1 day, depth held at parent's 6, tier 3->4, and each budget +1) \u2014 every one refused by its own name ['MANDATE_AMPLIFICATION_ALLOWLIST', 'MANDATE_AMPLIFICATION_BUDGET', 'MANDATE_AMPLIFICATION_CAP', 'MANDATE_AMPLIFICATION_CAPS', 'MANDATE_AMPLIFICATION_DEPTH', 'MANDATE_AMPLIFICATION_EXPIRY', 'MANDATE_AMPLIFICATION_TIER'], in component order; after each refusal the canon of roots/nodes/checks/spawns/records is byte-identical to before, with exactly one refusal_receipt appended naming the component and carrying the comparison"
  },
  {
   "id": "S3",
   "name": "budget boundaries exact: direct Nth passes, N+1th refuses; rate Nth passes, N+1th refuses in-window; spawn_close frees EXACTLY one slot",
   "verdict": "pass",
   "evidence": "direct: max_children=2 \u2014 spawns 1-2 recorded, 3rd check SPAWN_BUDGET_EXHAUSTED (direct). subtree: max_subtree=3 filled A+B+A1, next check refused (subtree); spawn_close(A1) freed_subtree_slots == 1 and active_descendants 3->2; exactly one further spawn passed, the next refused again; double-close refused NODE_ALREADY_CLOSED (frees nothing); closing the root refused ROOT_CLOSE_INVALID. rate: spawn_rate=2 \u2014 spawns at t0 and t0+30s recorded, check at t0+60s SPAWN_RATE_EXCEEDED, check at t0+3601s PASS"
  },
  {
   "id": "S4",
   "name": "chain-laundering: spawns split across siblings + grandchildren \u2014 ancestor subtree arithmetic still refuses at the true boundary",
   "verdict": "pass",
   "evidence": "CHAIN-LAUNDERING battery: root max_subtree=4 filled by splitting \u2014 2 children under the root, then one grandchild under EACH child (no single parent over any local limit). Three evasion attempts (a second grandchild under child A, a second under child B, and a great-grandchild under A1) each refused SPAWN_BUDGET_EXHAUSTED naming the ROOT's subtree arithmetic, chain state byte-identical after each; active_descendants stayed exactly 4 \u2014 the cap binds at the true subtree boundary regardless of how spawns are distributed across siblings"
  },
  {
   "id": "S5",
   "name": "revocation cascade mid-chain: all descendants PARENT_MANDATE_REVOKED, siblings unaffected",
   "verdict": "pass",
   "evidence": "mid-chain revoke of A under a SIGNED owner approval (engine.make_owner_approval, bench owner key) cascade-marked exactly {A, A1, A2} ROOT_REVOKED; spawn_check under A, A1 and A2 each refused PARENT_MANDATE_REVOKED, and an unchecked spawn_record under A refused the same; sibling branch B (and the root) unmarked in state and B's next spawn_check PASSes \u2014 the cascade is subtree-exact, siblings unaffected"
  },
  {
   "id": "S6",
   "name": "expiry: child Exp > parent Exp refused MANDATE_AMPLIFICATION_EXPIRY while parent live; child under expired parent refused PARENT_MANDATE_EXPIRED",
   "verdict": "pass",
   "evidence": "parent exp 2026-11-01: child spawned 2026-10-26 under it, then at 2026-11-02 checks under BOTH the child node and the root itself refused PARENT_MANDATE_EXPIRED. Separately, under a live parent (exp 2028-03-01) a child proposing exp 2028-03-02 refused MANDATE_AMPLIFICATION_EXPIRY \u2014 the child's own expiry may never outlive the parent's, whether or not the parent is live today"
  },
  {
   "id": "S7",
   "name": "unchecked-spawn honesty + check-id discipline: no-check record flagged UNCHECKED_SPAWN and reported by chain_verify; bogus / mismatched / consumed check ids refused",
   "verdict": "pass",
   "evidence": "spawn_record with NO check id recorded the spawn with flags [UNCHECKED_SPAWN], chain_link_ref UNCHECKED and a warning on the return; chain_verify on that node reports exactly its spawn id in unchecked_spawns (verdict VERIFIED alongside \u2014 the flag is reported, not whitewashed), chain_verify on the root reports it too, and chain_verify on a properly checked sibling reports none. A bogus check id refused CHAIN_UNVERIFIED; the real check re-presented under a different parent and under a different child card each refused CHAIN_UNVERIFIED and did NOT consume it \u2014 the correct record then succeeded, and replaying the consumed id refused CHECK_ALREADY_CONSUMED"
  },
  {
   "id": "S8",
   "name": "check-record completeness: passing check cites the full 9-component comparison (parent value, child value, verdict per component), persisted identically in the check record + ledger",
   "verdict": "pass",
   "evidence": "the passing check cites exactly 9 comparison rows in component order (CAPS, CAP, ALLOWLIST, EXPIRY, DEPTH, TIER, BUDGET:max_children, BUDGET:max_subtree, BUDGET:spawn_rate); every row carries parent value, child value and verdict \u2014 spot-verified against the suite's own proposed values (cap 2000->777, depth 6->5, tier 3->1, spawn_rate 50->7, exp, caps, allow). The identical comparison (canonical bytes) is persisted in the stored check AND in the hash-chained ChainLink ledger record, whose provenance carries record_id/seq/prev_hash/record_hash/actor/authority_ref/source_refs \u2014 a verdict without its comparison does not exist here"
  },
  {
   "id": "S9",
   "name": "effective mandate at depth 6 == suite's hand-computed path minimum incl. mid-chain tier narrowing + budgets path minimum",
   "verdict": "pass",
   "evidence": "depth-6 chain built with suite-fixed narrowings (cap 5000->4000->3000->1250, allow c dropped at L1 and b at L5, tier 4->2 mid-chain at L2, caps deploy dropped at L3 and write at L6, exp pulled to 2027-12-01 at L3 and 2027-06-15 at L6): node_effective_mandate == the suite's hand-computed path minimum {caps [audit, read, spawn], cap 1250, allow [bench-a], exp 2027-06-15, depth 2, tier 2} exactly, budgets_path_minimum == the componentwise min over the 7 path budget sets {3, 33, 42}, path and ancestry both 7 nodes root-first"
  },
  {
   "id": "S10",
   "name": "root_register gates: unsigned / tampered / hash-stripped artifacts ROOT_NOT_OWNER_SIGNED; missing or mismatched owner approval refused; duplicate registration refused",
   "verdict": "pass",
   "evidence": "root_register without an owner signature: an unsigned Wallet-shaped artifact, a signed artifact with the mandate cap tampered after signing, and a signed artifact with artifact_hash stripped each refused ROOT_NOT_OWNER_SIGNED; a valid signed artifact presented with NO owner_approval_ref and with a MISMATCHED one each refused OWNER_APPROVAL_REQUIRED; roots count unchanged through all five refusals; the valid registration then succeeded and re-registering the same mandate_id refused ROOT_ALREADY_REGISTERED \u2014 root registration is genuinely signature-gated (contrast S16)"
  },
  {
   "id": "S11",
   "name": "foreign chains + N-check chain half: valid foreign chain VERIFIED_AS_EVIDENCE only, never registered; amplified foreign link named; LangGraph/CrewAI config-shaped fixtures CHAIN_UNVERIFIED, never trusted",
   "verdict": "pass",
   "evidence": "a valid foreign chain (bench owner-signed root + 2 attenuating links) verifies VERIFIED_AS_EVIDENCE with the 'never registered as a trusted root' note; roots/nodes counts unchanged and spawn_check against its would-be root id refuses CHAIN_UNVERIFIED. A foreign link amplifying tier is named MANDATE_AMPLIFICATION_TIER in the evidence verdict; a well-formed artifact with a garbage signature verdicts ROOT_NOT_OWNER_SIGNED. N-CHECK chain half: LangGraph-style (max_depth/recursion_limit config + parent-trust spawn logs) and CrewAI-style (hierarchical crew + spawn logs) fixtures each verdict CHAIN_UNVERIFIED as presented chains, are refused as root_register artifacts, and their config-asserted parent names refuse spawn_check with CHAIN_UNVERIFIED \u2014 config assertions and trusted spawn logs are not chains; only a walk to an owner-signed root is"
  },
  {
   "id": "S12",
   "name": "novelty surface audit: tool surface is exactly the 11 governance verbs; builder denylist + task-named override verbs + renamed vendor verbs all absent (-32602, one shape); renamed-verb survival < 80% threshold",
   "verdict": "pass",
   "evidence": "tools/list is exactly the 11 governance verbs (5 operator + 6 agent); no name carries a mint / issue / create / dispatch / override / raise / skip stem. All 24 builder-enumerated forbidden names (incl. the task-named spawn_override, budget_raise, check_skip) refuse -32602 in ONE identical message shape. NOVELTY surface half: 32 renamed vendor verbs (LangGraph/CrewAI-shaped: create_agent, spawn_subagent, delegate_task, hire_agent, invoke_graph, mint_mandate, raise_budget, bypass_check, trust_root, ...) \u2014 32/32 refused -32602 (0% survival, far under the 80% fail threshold): Span has no vendor-shaped spawn/dispatch surface to rename into. Mechanism: Span checks a proposed spawn against a total componentwise attenuation order (6 mandate components + 3 budgets) walked to an owner-signed root, and enforces subtree budgets at EVERY ancestor \u2014 so authority arithmetic is computed from signed records across all siblings, never asserted by a framework config or trusted from a parent's spawn log."
  },
  {
   "id": "S13",
   "name": "hostile fuzz: envelope shapes, tuple fields wrong-typed, allowlist junk, nested objects, coerce_int_param probes on depth/budget fields, offset-less parse_time \u2014 named refusals, no -32603 / raw-exception escape",
   "verdict": "pass",
   "evidence": "ZERO escapes across the full battery: envelope shapes (non-object params/arguments, list/dict/int/null tool names), tuple/allowlist/coercion fuzz, offset-less timestamps, ref fuzz across ALL tools (spawn_check/spawn_record/spawn_close/ancestry/subtree/chain_verify/node_effective_mandate/root_revoke/root_register/budget_set/chain_view \u2014 list/dict refs), and non-object presented-chain links (str/null/list/int entries) \u2014 every probe a named refusal or -32602 shape, ZERO -32603, and chain state byte-identical after the battery (refusal receipts excepted); server still healthy and a valid spawn_check PASSes after"
  },
  {
   "id": "S14",
   "name": "hash-chain tamper: node-tuple tamper flips chain_verify to CHAIN_UNVERIFIED; ledger-record tamper flips ledger_chain_valid to false AND downgrades the verdict to CHAIN_UNVERIFIED (never VERIFIED); restore re-verifies",
   "verdict": "pass",
   "evidence": "node-tuple tamper (caps + 'bench-evil' written directly into the state file): chain_verify flips to CHAIN_UNVERIFIED naming MANDATE_AMPLIFICATION_CAPS on the tampered link \u2014 recomputation, not trust. Ledger-record tamper (first record's cap rewritten): chain_view and chain_verify both report ledger_chain_valid false AND chain_verify's verdict itself downgrades to CHAIN_UNVERIFIED \u2014 never VERIFIED alongside ledger_chain_valid=false (S14 closure). Restoring the original bytes re-verifies both (VERIFIED + ledger_chain_valid true)"
  },
  {
   "id": "S15",
   "name": "deviation adjudications: DEPTH_STEP_INVALID (+ MANDATE_AMPLIFICATION_DEPTH) behavior; chain_verify flag scope = path + own subtree; spawn_rate window is exactly 3600s at the boundary",
   "verdict": "pass",
   "evidence": "RULINGS (full text in NOTES): (1) DEPTH_STEP_INVALID UPHELD \u2014 child depth 4 under parent depth 6 (a skipped step) refuses DEPTH_STEP_INVALID, child depth 7 refuses MANDATE_AMPLIFICATION_DEPTH, the exact step 5 PASSes, and a depth-0 parent refuses DEPTH_EXHAUSTED: spec 3.1's exact-equality depth law is enforced literally under two added, pattern-consistent names. (2) Flag scope ACCEPTED \u2014 the unchecked spawn under A is reported by chain_verify on A, on the unchecked node itself and on the root, and NOT by a verify scoped to sibling B: scoped reporting, not whitewash. (3) Window ACCEPTED \u2014 spawn_rate=1: a check at +3599s refuses SPAWN_RATE_EXCEEDED and at exactly +3600s PASSes, so the builder's fixed window is precisely 3600s with an exact boundary; per-mandate window configurability backlogged (low)"
  },
  {
   "id": "S16",
   "name": "T3 gate: root_revoke / budget_set require a VERIFIED owner-signed approval \u2014 fabricated string, wrong-key, expired, tampered and action/target/params-mismatched approvals all refuse OWNER_APPROVAL_INVALID (refusal-only, state identical); genuine signed approvals succeed for both verbs",
   "verdict": "pass",
   "evidence": "T3 gate is a VERIFIED owner signature, not presence: 18 probes \u2014 for root_revoke AND budget_set each: fabricated bare string, wrong-key, expired, tampered (post-sign expiry change), action-mismatched, target-mismatched and params-mismatched approvals all refuse OWNER_APPROVAL_INVALID, refusal-only with chain state byte-identical after every probe; no approval at all refuses OWNER_APPROVAL_REQUIRED; genuine approvals signed by the bench owner key via engine.make_owner_approval (action/target/params/expiry, key pinned at root_register) SUCCEED for both \u2014 revoke cascades {root, child} and budget_set narrows the root's budgets to {'max_children': 1, 'max_subtree': 40, 'spawn_rate': 50} in state"
  },
  {
   "id": "U1",
   "name": "auth enforced",
   "verdict": "na",
   "evidence": "local stdio transport \u2014 process ownership is the boundary; no credential layer exists to test (standing house note). Surface-level T3 reachability within that boundary is scored in S16"
  },
  {
   "id": "U2",
   "name": "schema valid",
   "verdict": "pass",
   "evidence": "initialize returns protocolVersion 2025-03-26 + serverInfo (eclipse-span 0.1.0); all 11 tools declare object inputSchemas, required a subset of properties, each described; the standing descriptor 'delegation-chain governance: spawn limits, depth caps, mandate attenuation' travels verbatim in the README and the Bazaar manifest tagline"
  },
  {
   "id": "U3",
   "name": "no network surface (source-level)",
   "verdict": "pass",
   "evidence": "source-level: server.py, span_engine.py and signing.py contain no socket/urllib/requests/http.client/httpx/urlopen/subprocess references \u2014 stdio-only, stdlib imports (hashlib/json/datetime/decimal/base64)"
  },
  {
   "id": "U4",
   "name": "no secret leak",
   "verdict": "pass",
   "evidence": "secret scan over 388 collected outputs and the full bench state file: no private-key blocks, authorization headers, bearer or sim tokens; and neither the bench owner private scalar nor the Span signing private scalar appears in any output or in the state file \u2014 only public keys, signatures and hashes travel"
  },
  {
   "id": "U5",
   "name": "sane errors",
   "verdict": "pass",
   "evidence": "every schema-valid foreseeable miss is refused BY NAME: unknown node -> NODE_NOT_FOUND on ancestry / subtree / spawn_close / budget_set / root_revoke; unwalkable node -> CHAIN_UNVERIFIED on node_effective_mandate / chain_verify / spawn_check; empty root_register -> OWNER_APPROVAL_REQUIRED; spawn_check missing child fields -> INPUT_REQUIRED (after the parent walk). The schema-invalid escape class is scored separately in S13"
  },
  {
   "id": "U6",
   "name": "process health",
   "verdict": "pass",
   "evidence": "initialize latency 100.0 ms; ping answers {}; a malformed non-JSON line is answered with an error and the process keeps serving (ping ok after drain); unknown method refused -32601 method-not-found"
  },
  {
   "id": "U7",
   "name": "audit completeness (record + receipt chains recomputed in-suite, signatures verified)",
   "verdict": "pass",
   "evidence": "both chains recomputed IN-SUITE from the state file: the refusal-receipt chain (98 receipts, 98 refusals) is GENESIS-anchored with every hash + prev_hash valid, and a one-field forgery in a copy breaks it; the ledger record chain (175 records) recomputes every record_hash AND verifies every record's ECDSA signature against the bench Span pubkey \u2014 record types present {'RootMandateRef': 29, 'ChainLink': 73, 'SpawnRecord': 69, 'SpawnClose': 1, 'RootRevoke': 2, 'BudgetSet': 1}; every state change and every refusal in the run is receipted, append-only"
  },
  {
   "id": "U8",
   "name": "bench isolation",
   "verdict": "pass",
   "evidence": "workspace product state byte-identical before/after bench"
  }
 ],
 "notes": [
  "Local scope: stdio MCP over bench-owned /tmp state and a bench-owned Span signing key + bench-owned owner key; no Wallet process is called \u2014 root artifacts are Wallet-shaped fixtures the suite constructs and signs itself. Blocking enforcement inside external frameworks and cross-org trust are gated (spec 5.5) and in the manifest not_tested verbatim.",
  "U1 is na: local stdio transport \u2014 process ownership is the boundary (standing house note). T3 reachability WITHIN that boundary is nevertheless scored as a product check (S16), because spec 3.6 promises disjoint dispatch tables, not merely a process boundary.",
  "NOVELTY (owner law): mechanism \u2014 Span checks a proposed spawn against a total componentwise attenuation order (6 mandate components + 3 budgets) walked to an owner-signed root, and enforces subtree budgets at EVERY ancestor \u2014 so authority arithmetic is computed from signed records across all siblings, never asserted by a framework config or trusted from a parent's spawn log. Distinguishing result (S11/S12): a LangGraph-style config (max_depth + recursion_limit + parent-trust spawn logs) and a CrewAI-style hierarchical crew config are both refused as chains (CHAIN_UNVERIFIED as presented chains; named refusal as root artifacts) \u2014 a framework's config assertion is not evidence \u2014 and 100% of renamed vendor verbs probed (create_agent / spawn_subagent / delegate_task / hire_agent / mint_mandate / raise_budget / bypass_check / ...) are absent (-32602, one shape). A config-file governance tool accepts its own config as truth and fails both halves. NOVELTY_PROVEN on the mechanism; the verdict below is driven by S13/S16, not by novelty.",
  "DEVIATION RULINGS (bench adjudication, S15): (1) DEPTH_STEP_INVALID \u2014 UPHELD, not a defect. Spec 3.1 fixes Depth_child = Depth_parent - 1 EXACTLY; the spec's refusal vocabulary names neither depth-step case (child depth == parent amplifies; child depth skipping a step violates the equality). The builder's two added names (MANDATE_AMPLIFICATION_DEPTH for the amplifying case, DEPTH_STEP_INVALID for the skipping case) are pattern-consistent, deterministic, first-violation-ordered, and the behavior they name is the spec's equation enforced literally. Spec-vocabulary gap recorded for the wave-2 spec errata, product behavior conforms. (2) chain_verify flag scope (node path + own subtree) \u2014 ACCEPTED for v0: a verify of the affected node, of any descendant of it, or of the root reports the UNCHECKED_SPAWN flag; a verify scoped to an unaffected sibling branch does not \u2014 which is correct scoping, not whitewash, because the verdict a chain_verify returns is about the chain it walked. Consumers wanting a global flag audit verify at the root (asserted in S15). (3) spawn_rate window fixed at 3600s \u2014 ACCEPTED: spec 5.1 says 'children per window' without naming a duration; the builder's 3600s is uniform, is stated in the refusal text, and its boundary is exact (a spawn at +3599s counts, at +3600s it has aged out \u2014 S15). Backlog (low): the window is a global constant, not a per-mandate parameter \u2014 an owner who needs a different window cannot express one.",
  "S14 CLOSED (re-bench 2026-10-26): after a ledger-record tamper, chain_verify's verdict itself downgrades to CHAIN_UNVERIFIED alongside ledger_chain_valid: false \u2014 a VERIFIED verdict never coexists with a broken ledger. S14 now asserts the downgrade explicitly.",
  "S13 CLOSED (re-bench 2026-10-26): envelope + reference guards now refuse by name / -32602 shape across all tools \u2014 non-object params/arguments, list/dict/int tool names, list/dict refs on every ref-taking tool, and non-object presented-chain links all refuse named with ZERO -32603 and chain state (receipts excepted) byte-identical.",
  "Run 1 card retained append-only: its S12 was untestable on a bench-suite format-string bug of mine (3 arguments for 4 %d placeholders in the evidence string \u2014 the surface audit's own assertions had already run); fixed for run 2. Run 2 card (FAILED 21/24, S13/S14-observation/S16) retained append-only; this re-bench appends a new card. No product code was touched by the bench in any run.",
  "NEW DEFECT S17 (major, found re-bench run 3, card retained): server.py's key-material scanner flags owner_approval.params_hash \u2014 a SHA-256 hex (bare_64_hex) that the documented T3 approval format MANDATES \u2014 as key material; SCAN_SKIP_KEYS exempts signature / artifact_hash / wallet_* but not params_hash. Every signed approval, valid or invalid, is refused KEY_MATERIAL_REFUSED at $.owner_approval.params_hash at the transport BEFORE engine verification, so genuine root_revoke / budget_set are unreachable via MCP and U7's census loses its RootRevoke / BudgetSet records. Isolation (bench engine-direct probe, /tmp state): genuine signed budget_set + root_revoke SUCCEED and a forged string refuses OWNER_APPROVAL_INVALID engine-direct \u2014 the engine's S16 law is correct; the defect is the transport scanner's false positive on a hash field. Fix is Silas's (exempt the params_hash field name \u2014 a hash is not key material); bench does not fix product code. S16 therefore closes at engine level only, NOT end-to-end. S13/S14 closures unaffected.",
  "S16 ENGINE LAW + SUITE MAINTENANCE (re-bench 2026-10-26, bench-side only, no check weakened): the S16 law change makes a bare owner_approval_ref string invalid BY DESIGN for root_revoke / budget_set. Legitimate calls now present SIGNED owner approvals constructed with the bench owner key via engine.make_owner_approval(owner_d, 'root_revoke'|'budget_set', target_ref, params, expiry) in the documented format \u2014 S5's legitimate cascade revoke and S16's legitimate budget_set (the signed BudgetSet + RootRevoke records U7's census recomputes). The FORGED-string probes stay exactly as hostile as before, joined by wrong-key / expired / tampered / action-, target- and params-mismatched signed artifacts \u2014 all must refuse OWNER_APPROVAL_INVALID, refusal-only, state identical; genuine signed approvals must succeed for both verbs."
 ],
 "history": "append-only; prior runs retained in results/"
}