{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://vibepro.local/schemas/spec.json",
  "title": "VibePro Inferred Spec",
  "description": "AI-authored spec produced from Story+Code+Test fingerprints. VibePro CLI validates and stores this artifact; humans do not edit it directly.",
  "type": "object",
  "required": [
    "schema_version",
    "story_id",
    "clauses"
  ],
  "properties": {
    "schema_version": {
      "type": "string",
      "const": "0.1.0"
    },
    "story_id": {
      "type": "string",
      "minLength": 1
    },
    "generated_at": {
      "type": "string",
      "description": "ISO 8601 timestamp. Filled by validator if absent."
    },
    "generated_by": {
      "type": "object",
      "properties": {
        "caller": {
          "type": "string",
          "description": "claude-code | codex | other identifier"
        },
        "stage": {
          "type": "string",
          "enum": [
            "ai_synthesis",
            "static_fallback"
          ]
        }
      }
    },
    "inputs_digest": {
      "type": "object",
      "properties": {
        "story_sha": {
          "type": "string"
        },
        "code_sha": {
          "type": "string"
        },
        "test_sha": {
          "type": "string"
        }
      }
    },
    "previous_spec_id": {
      "type": [
        "string",
        "null"
      ]
    },
    "semantic_contract": {
      "$ref": "#/$defs/semantic_contract",
      "description": "Optional semantic sufficiency contract. When present, the model declares meaning and the host validates only its deterministic shape and contradictions. Legacy Specs remain valid but report semantic_contract_missing."
    },
    "clauses": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/clause"
      }
    },
    "diagrams": {
      "type": "array",
      "description": "Design diagrams required by the change type. See diagram-requirement-resolver for which kinds are mandatory.",
      "items": {
        "$ref": "#/$defs/diagram"
      }
    },
    "open_questions": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "id",
          "question"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "question": {
            "type": "string",
            "minLength": 1
          },
          "blocker": {
            "type": "boolean"
          }
        }
      }
    }
  },
  "$defs": {
    "clause": {
      "type": "object",
      "required": [
        "id",
        "type",
        "statement",
        "origin"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "Clause id. Use INV-<n>, S-<n>, C-<n>, SLA-<n>. New clauses may use INV-NEW-<n> placeholders; validator assigns stable ids."
        },
        "type": {
          "type": "string",
          "enum": [
            "invariant",
            "scenario",
            "contract",
            "sla"
          ]
        },
        "statement": {
          "type": "string",
          "minLength": 4,
          "description": "One-sentence machine-checkable statement. Avoid ambiguity."
        },
        "rationale": {
          "type": "string",
          "description": "Why this clause exists. Cite story acceptance criterion / code anchor."
        },
        "origin": {
          "type": "object",
          "description": "Where this clause was derived from. At least one of story_refs / architecture_refs / code_refs / test_refs must be non-empty.",
          "properties": {
            "story_refs": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "kind"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "acceptance_criteria",
                      "background",
                      "policy",
                      "frontmatter",
                      "other"
                    ]
                  },
                  "index": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "text_snippet": {
                    "type": "string"
                  }
                }
              }
            },
            "architecture_refs": {
              "type": "array",
              "description": "Architecture, IA, route-flow, state, or boundary evidence that shaped this clause.",
              "items": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "minLength": 1
                  },
                  "section": {
                    "type": "string"
                  },
                  "text_snippet": {
                    "type": "string"
                  }
                }
              }
            },
            "code_refs": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "minLength": 1
                  },
                  "anchor": {
                    "type": "string",
                    "description": "Identifier or substring that should appear in the file."
                  },
                  "line": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            },
            "test_refs": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "minLength": 1
                  },
                  "case": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "confidence": {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "outcome_kind": {
          "type": "string",
          "enum": [
            "internal_output",
            "downstream_outcome",
            "canonical_readback"
          ],
          "description": "The independently observable outcome boundary. Missing legacy values are projected as unknown."
        },
        "case_kind": {
          "type": "string",
          "enum": [
            "positive",
            "negative",
            "ambiguous",
            "conflicting",
            "stale",
            "unavailable"
          ],
          "description": "Whether this clause is a happy path or a declared counterexample/unavailable case."
        },
        "semantic_evaluation": {
          "$ref": "#/$defs/semantic_evaluation",
          "description": "Model-declared semantic assessment. It is not execution evidence or a proof of user-visible outcome."
        },
        "verifiable_by": {
          "type": "object",
          "description": "Deterministic patterns the drift detector uses. Optional but recommended.",
          "properties": {
            "code_pattern": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/pattern"
              }
            },
            "test_pattern": {
              "type": "array",
              "items": {
                "$ref": "#/$defs/pattern"
              }
            }
          }
        },
        "first_seen_at": {
          "type": "string"
        },
        "last_revised_at": {
          "type": "string"
        }
      }
    },
    "diagram": {
      "type": "object",
      "required": [
        "kind",
        "mermaid"
      ],
      "properties": {
        "kind": {
          "type": "string",
          "enum": [
            "er",
            "state",
            "sequence",
            "flow",
            "c4_context",
            "deployment",
            "threat_model",
            "dfd"
          ]
        },
        "mermaid": {
          "type": "string",
          "minLength": 1,
          "description": "Mermaid source. First non-empty line must match the kind-specific prefix."
        },
        "entities": {
          "type": "array",
          "description": "Names that appear in the diagram. Required non-empty for er/state/sequence/c4_context.",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "rationale": {
          "type": "string",
          "description": "Why this diagram exists. Cite story acceptance criterion / code anchor."
        }
      }
    },
    "pattern": {
      "type": "object",
      "required": [
        "file_glob"
      ],
      "properties": {
        "file_glob": {
          "type": "string",
          "description": "Glob relative to repo root. Examples: src/**/billing*.ts, test/**/*.test.{js,ts}"
        },
        "must_contain": {
          "type": "string",
          "description": "Substring (case-sensitive) that must appear in at least one matched file."
        },
        "must_not_contain": {
          "type": "string",
          "description": "Substring (case-sensitive) that must not appear in any matched file."
        },
        "must_cover": {
          "type": "string",
          "description": "Identifier or substring that must appear in test names or assertion text across matched files."
        }
      }
    },
    "semantic_contract": {
      "type": "object",
      "required": [
        "semantic_owner",
        "deterministic_owner",
        "core_hypothesis",
        "strongest_counterexample",
        "false_success_mode",
        "unknown_representation",
        "operational_definitions"
      ],
      "properties": {
        "semantic_owner": {
          "type": "string",
          "minLength": 1
        },
        "deterministic_owner": {
          "type": "string",
          "minLength": 1
        },
        "core_hypothesis": {
          "type": "string",
          "minLength": 1
        },
        "strongest_counterexample": {
          "type": "string",
          "minLength": 1
        },
        "false_success_mode": {
          "type": "string",
          "minLength": 1
        },
        "unknown_representation": {
          "type": "string",
          "minLength": 1
        },
        "operational_definitions": {
          "type": "array",
          "minItems": 7,
          "description": "Operational definitions for sufficient, relevant, directly_answers, applicable, current, non_conflicting, and unambiguous.",
          "items": {
            "$ref": "#/$defs/operational_definition"
          }
        },
        "unresolved_behavior": {
          "$ref": "#/$defs/unresolved_behavior"
        }
      },
      "additionalProperties": true
    },
    "operational_definition": {
      "type": "object",
      "required": [
        "term",
        "definition"
      ],
      "properties": {
        "term": {
          "type": "string",
          "minLength": 1
        },
        "definition": {
          "type": "string",
          "minLength": 1
        }
      },
      "additionalProperties": true
    },
    "unresolved_behavior": {
      "type": "object",
      "required": [
        "action",
        "max_questions",
        "automatic_continuation"
      ],
      "properties": {
        "action": {
          "const": "ask_once"
        },
        "max_questions": {
          "const": 1
        },
        "automatic_continuation": {
          "const": false
        }
      },
      "additionalProperties": true
    },
    "semantic_evaluation": {
      "type": "object",
      "required": [
        "answer_status"
      ],
      "properties": {
        "answer_status": {
          "type": "string",
          "enum": [
            "resolved",
            "ambiguous",
            "no_answer",
            "conflicting",
            "stale",
            "unavailable"
          ]
        },
        "checks": {
          "type": "object",
          "required": [
            "relevant",
            "directly_answers",
            "applicable",
            "current",
            "non_conflicting",
            "unambiguous",
            "sufficient"
          ],
          "properties": {
            "relevant": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "directly_answers": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "applicable": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "current": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "non_conflicting": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "unambiguous": {
              "type": [
                "boolean",
                "null"
              ]
            },
            "sufficient": {
              "type": [
                "boolean",
                "null"
              ]
            }
          },
          "additionalProperties": true
        },
        "supporting_record_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "question_digest": {
          "type": "string",
          "minLength": 1
        },
        "normalized_choices": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "selected_choice": {
          "type": "string",
          "minLength": 1
        },
        "derived_answer": {
          "type": "string",
          "minLength": 1
        },
        "relevance_explanation": {
          "type": "string",
          "minLength": 1
        },
        "applicability_scope": {
          "type": "string",
          "minLength": 1
        },
        "confidence": {
          "type": [
            "number",
            "null"
          ],
          "minimum": 0,
          "maximum": 1
        },
        "conflict_check": {
          "type": "string",
          "enum": [
            "clear",
            "conflicting",
            "unknown"
          ]
        },
        "freshness_check": {
          "type": "string",
          "enum": [
            "current",
            "stale",
            "unknown"
          ]
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "answer_status": {
                "const": "resolved"
              }
            },
            "required": [
              "answer_status"
            ]
          },
          "then": {
            "required": [
              "question_digest",
              "supporting_record_ids",
              "relevance_explanation",
              "applicability_scope",
              "confidence",
              "conflict_check",
              "freshness_check",
              "checks"
            ],
            "anyOf": [
              {
                "required": [
                  "derived_answer"
                ]
              },
              {
                "required": [
                  "normalized_choices",
                  "selected_choice"
                ]
              }
            ]
          }
        }
      ],
      "additionalProperties": true,
      "description": "Minimal model-declared semantic assessment. Null preserves unknown."
    }
  }
}
