Fail-closed fighter-clearance Slack agent with deterministic rule engine, tamper-evident audit ledger, and formal Z3 verification
CornerCheck demonstrates strong domain-specific design with clear, purposeful tool names and comprehensive descriptions (avg 180 chars). All 6 tools have explicit input schemas with typed parameters and descriptions. Error handling is sophisticated, the _safe_tool wrapper ensures infrastructure failures return a fail-closed ERROR envelope with is_clearance: False, preventing misinterpretation. However, output schemas are not formally documented (only visible through code inspection), and some parameter descriptions lack constraint details (e.g., date format, jurisdiction enum values). Tool composition is excellent: each tool has a single responsibility, and outputs chain naturally (fighter_id flows from er_resolve_fighter → er_fighter_details → rules_evaluate_clearance). The ledger_record_clearance tool includes guarded re-validation, demonstrating security-aware design. Naming follows verb_noun convention consistently (er_*, rules_*, ledger_*). Main gaps: no pagination support (not needed for this domain), missing enum constraints for jurisdiction/outcome/decision fields, and output schemas not formally declared in tool definitions.
Full detail for one fighter: profile plus every suspension on record, each with its source citation.
Resolve a fighter name to candidates. CONFIRMED only on a unique high-confidence match; identical names always come back AMBIGUOUS for a human pick; low confidence refuses (NOT_FOUND). Never guess past this tool.
Most recent audit-ledger entries, newest first.
Record a clearance decision in the tamper-evident audit ledger. GUARDED: the server re-runs the rule engine and refuses any decision that contradicts it; refusals are themselves ledgered. Requires the thread_key provided in the conversation context.
THE clearance decision: deterministic rule engine over recorded suspensions. DO_NOT_CLEAR lists every active suspension with citations and, for cross-jurisdiction holds, a sport-aware consultation note (15 U.S.C. 6306(b) is binding for boxing; MMA has no federal equivalent).
Output schemas not formally documented in tool definitions. Schemas are inferred from code (e.g., _suspension_dict returns dict with type/start/end/indefinite/jurisdiction/reason/source_url), but LLMs cannot see these without inspecting implementation. FastMCP should declare outputSchema in @mcp.tool decorators.
Enum constraints missing for domain-critical parameters. 'outcome' in rules_outcome_window accepts free-form string (should be enum: TKO|KO|KO_LOC). 'decision' in ledger_record_clearance accepts free-form string (should be enum: CLEAR|DO_NOT_CLEAR). 'target_jurisdiction' lacks enum of valid jurisdictions. LLMs will hallucinate invalid values.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 58 | <=2025-11-25 | v2 |
Mandated minimum window (days) after a bout outcome: TKO, KO, or KO_LOC (KO with loss of consciousness). cause='head_shot_stoppage' applies the ABC BSI overlay; sparring=true returns the CornerCheck no-contact layer (not an ABC mandate).
Date parameter format not formally specified. 'on_date' in rules_evaluate_clearance and ledger_record_clearance documented as 'ISO date (YYYY-MM-DD)' in description, but no JSON Schema format constraint (format: 'date'). Parameter descriptions should include format constraints, not just prose.
thread_key parameter in ledger_record_clearance documented as 'from conversation context' but no guidance on how LLM obtains it. If thread_key is not passed by the MCP client in _meta or context, the tool will fail. Dependency should be explicit: 'Provided by the MCP client in request context; required for audit chain verification.'
Error recovery guidance incomplete. While _safe_tool wrapper returns ERROR envelope with is_clearance: False, parameter validation errors (e.g., invalid fighter_id format, malformed date) are not caught before the tool runs. Add input validation with actionable error messages: 'Invalid fighter_id: must be a valid UUID. Try er_resolve_fighter() first to get a valid ID.'