diff --git a/talent/participation.md b/talent/participation.md index 1667ceddc..c11c0165a 100644 --- a/talent/participation.md +++ b/talent/participation.md @@ -8,6 +8,7 @@ "priority": 10, "tier": 3, "output": "json", + "schema": "participation.schema.json", "load": { "transcripts": true, "percepts": true, diff --git a/talent/participation.schema.json b/talent/participation.schema.json new file mode 100644 index 000000000..cf12a44e8 --- /dev/null +++ b/talent/participation.schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "additionalProperties": false, + "required": ["participation"], + "properties": { + "participation": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["name", "role", "source", "confidence", "context", "entity_id"], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "enum": ["attendee", "mentioned"] + }, + "source": { + "type": "string", + "enum": ["voice", "speaker_label", "transcript", "screen", "other"] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "context": { + "type": "string" + }, + "entity_id": { + "type": "null" + } + } + } + }, + "participation_confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + } +} diff --git a/talent/participation_entry.schema.json b/talent/participation_entry.schema.json new file mode 100644 index 000000000..7d104387c --- /dev/null +++ b/talent/participation_entry.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "additionalProperties": false, + "required": ["name", "role", "source", "confidence", "context", "entity_id"], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "role": { + "type": "string", + "enum": ["attendee", "mentioned"] + }, + "source": { + "type": "string", + "enum": ["voice", "speaker_label", "transcript", "screen", "other"] + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "context": { + "type": "string" + }, + "entity_id": { + "type": "null" + } + } +} diff --git a/tests/baselines/api/stats/stats.json b/tests/baselines/api/stats/stats.json index a700baf68..fb3e93d3a 100644 --- a/tests/baselines/api/stats/stats.json +++ b/tests/baselines/api/stats/stats.json @@ -172,6 +172,7 @@ "path": "/talent/participation.md", "priority": 10, "schedule": "activity", + "schema": "participation.schema.json", "source": "system", "tier": 3, "title": "Participation", diff --git a/tests/test_participation_schema.py b/tests/test_participation_schema.py new file mode 100644 index 000000000..4eb36a17b --- /dev/null +++ b/tests/test_participation_schema.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: AGPL-3.0-only +# Copyright (c) 2026 sol pbc + +import json +from pathlib import Path + +from jsonschema import Draft202012Validator + +from think.talent import get_talent + +TALENT_DIR = Path(__file__).resolve().parents[1] / "talent" +PARTICIPATION_ENTRY_SCHEMA_PATH = TALENT_DIR / "participation_entry.schema.json" +PARTICIPATION_SCHEMA_PATH = TALENT_DIR / "participation.schema.json" + + +def _load_json(path: Path) -> dict: + return json.loads(path.read_text(encoding="utf-8")) + + +def test_participation_entry_schema_is_valid_draft_2020_12(): + schema = _load_json(PARTICIPATION_ENTRY_SCHEMA_PATH) + + Draft202012Validator.check_schema(schema) + + assert schema["$schema"] == "https://json-schema.org/draft/2020-12/schema" + + +def test_participation_schema_is_valid_and_matches_loaded(): + schema = _load_json(PARTICIPATION_SCHEMA_PATH) + + Draft202012Validator.check_schema(schema) + + assert get_talent("participation")["json_schema"] == schema + + +def test_participation_schema_items_match_fragment(): + schema = _load_json(PARTICIPATION_SCHEMA_PATH) + fragment = _load_json(PARTICIPATION_ENTRY_SCHEMA_PATH) + + items = dict(schema["properties"]["participation"]["items"]) + fragment_without_schema = dict(fragment) + fragment_without_schema.pop("$schema") + + assert items == fragment_without_schema diff --git a/tests/test_participation_talent.py b/tests/test_participation_talent.py index 728e41767..8520e6ea7 100644 --- a/tests/test_participation_talent.py +++ b/tests/test_participation_talent.py @@ -15,6 +15,7 @@ def test_participation_talent_frontmatter_and_placeholders(): assert post.metadata["activities"] == ["*"] assert post.metadata["tier"] == 3 assert post.metadata["output"] == "json" + assert post.metadata["schema"] == "participation.schema.json" assert post.metadata["priority"] == 10 assert post.metadata["load"]["talents"]["sense"] is True