{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://casan.local/schemas/agentic-lifecycle.schema.json", "title": "CASAN Agentic Lifecycle Contract", "description": "Plan-20 client-agnostic lifecycle contract for the CASAN agentic bridge. Each request is a single JSON object read on stdin; each response is a single JSON object written on stdout. Client adapters (Claude Code, Codex, VS Code) translate their native hook payloads into these shapes and render bridge responses back into client-native JSON. The bridge NEVER calls a model — it only admits, gates, records evidence, and finalizes traces (single-model invariant, Plan-20 §3.1).", "type": "object", "required": ["op"], "properties": { "op": { "type": "string", "enum": ["begin", "pre-tool", "post-tool", "telemetry", "finalize", "abort"], "description": "Lifecycle operation to perform." } }, "allOf": [ { "if": { "properties": { "op": { "const": "begin" } } }, "then": { "$ref": "#/definitions/beginRequest" } }, { "if": { "properties": { "op": { "const": "pre-tool" } } }, "then": { "$ref": "#/definitions/preToolRequest" } }, { "if": { "properties": { "op": { "const": "post-tool" } } }, "then": { "$ref": "#/definitions/postToolRequest" } }, { "if": { "properties": { "op": { "const": "telemetry" } } }, "then": { "$ref": "#/definitions/telemetryRequest" } }, { "if": { "properties": { "op": { "const": "finalize" } } }, "then": { "$ref": "#/definitions/finalizeRequest" } }, { "if": { "properties": { "op": { "const": "abort" } } }, "then": { "$ref": "#/definitions/abortRequest" } } ], "definitions": { "clientContext": { "type": "object", "required": ["client"], "properties": { "client": { "type": "string", "enum": ["claude-code", "codex", "vscode", "unknown"], "description": "Agentic client family." }, "client_version": { "type": ["string", "null"] }, "adapter_version": { "type": ["string", "null"] }, "project": { "type": ["string", "null"], "description": "Project root as the client sees it; canonicalized by the bridge." }, "session": { "type": ["string", "null"], "description": "Client-native session id. Hashed by the bridge, never stored raw." }, "integration_mode": { "type": ["string", "null"], "enum": ["casan_owned", "managed_hook", "project_hook", "observed_only", null], "description": "Declared integration mode; the bridge may DOWNGRADE (never upgrade) it based on enforcement mode and coverage." } } }, "beginRequest": { "allOf": [{ "$ref": "#/definitions/clientContext" }], "required": ["op", "client", "prompt"], "properties": { "op": { "const": "begin" }, "prompt": { "type": "string", "description": "Raw user prompt. Scanned by H4 then discarded — only a salted hash is persisted." }, "turn": { "type": ["string", "null"], "description": "Optional client-native turn correlation id; hashed, not stored raw." } } }, "preToolRequest": { "required": ["op", "admission_id", "tool"], "properties": { "op": { "const": "pre-tool" }, "admission_id": { "type": "string" }, "tool": { "type": "string", "description": "Client-native tool name, e.g. Bash, Edit, Write, WebFetch." }, "tool_input": { "description": "Tool input payload. Scanned/redacted; only a hash + redacted summary are persisted." }, "project": { "type": ["string", "null"] } } }, "postToolRequest": { "required": ["op", "admission_id", "tool"], "properties": { "op": { "const": "post-tool" }, "admission_id": { "type": "string" }, "tool": { "type": "string" }, "status": { "type": ["string", "null"], "enum": ["success", "error", "denied", "timeout", null] }, "duration_ms": { "type": ["integer", "null"], "minimum": 0 }, "result": { "description": "Tool result. Never stored raw — hashed + redacted." } } }, "telemetryRequest": { "required": ["op", "admission_id"], "properties": { "op": { "const": "telemetry" }, "admission_id": { "type": "string" }, "model": { "type": ["string", "null"] }, "runtime_ms": { "type": ["integer", "null"], "minimum": 0 }, "input_tokens": { "type": ["integer", "null"], "minimum": 0 }, "output_tokens": { "type": ["integer", "null"], "minimum": 0 }, "cache_tokens": { "type": ["integer", "null"], "minimum": 0 }, "cost_amount": { "type": ["number", "null"], "minimum": 0 }, "cost_currency": { "type": ["string", "null"] }, "cost_source": { "type": ["string", "null"], "description": "Provenance of cost/token numbers, e.g. provider_reported, statusline_estimate, session_delta, unavailable. Numbers WITHOUT an accurate source MUST be null with a warning (Plan-20 §5).", "enum": [ "provider_reported", "sdk_result_message", "statusline_estimate", "session_delta", "unavailable", null ] } } }, "finalizeRequest": { "required": ["op", "admission_id"], "properties": { "op": { "const": "finalize" }, "admission_id": { "type": "string" }, "stop_reason": { "type": ["string", "null"], "enum": ["completed", "user_interrupt", "error", "max_turns", "timeout", null] }, "assistant_summary": { "type": ["string", "null"] }, "changed_files": { "type": ["array", "null"], "items": { "type": "string" } } } }, "abortRequest": { "required": ["op"], "properties": { "op": { "const": "abort" }, "admission_id": { "type": ["string", "null"] }, "reason": { "type": ["string", "null"] } } }, "bridgeResponse": { "type": "object", "required": ["op", "decision", "schema_version"], "properties": { "op": { "type": "string" }, "schema_version": { "type": "string" }, "decision": { "type": "string", "enum": ["allow", "block", "deny", "recorded", "certified", "non_certified", "error"] }, "admission_id": { "type": ["string", "null"] }, "trace_id": { "type": ["string", "null"] }, "integration_mode": { "type": ["string", "null"] }, "certification_strength": { "type": ["string", "null"], "enum": ["casan_owned", "managed_hook", "project_hook", "observed_only", null] }, "telemetry_quality": { "type": ["string", "null"], "enum": ["complete", "partial", "insufficient", null] }, "reason": { "type": ["string", "null"] }, "warnings": { "type": "array", "items": { "type": "string" } }, "context": { "type": ["string", "null"], "description": "Optional additional context the adapter may inject into the turn (e.g. certification banner)." } } } } }