schema.py python
74 lines 2.6 KB
Raw
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 63 days ago
1 """Frozen landing manifest types (K12)."""
2
3 from __future__ import annotations
4
5 from dataclasses import dataclass
6 from pathlib import Path
7 from typing import Any
8
9 import yaml
10
11
12 @dataclass(frozen=True)
13 class LandingManifest:
14 """Parsed ``docs/landing/manifest.yaml`` contract."""
15
16 version: int
17 license: str
18 section_ids: tuple[str, ...]
19 persona_ids: tuple[str, ...]
20 status_badges: frozenset[str]
21 funnel_steps: tuple[str, ...]
22
23
24 def load_manifest(manifest_path: Path) -> LandingManifest:
25 """Load and normalize the landing manifest."""
26 raw: dict[str, Any] = yaml.safe_load(manifest_path.read_text(encoding="utf-8"))
27 if not isinstance(raw, dict):
28 raise ValueError("manifest root must be a mapping")
29
30 version = raw.get("version")
31 if version != 1:
32 raise ValueError(f"unsupported manifest version: {version!r}")
33
34 license_id = raw.get("license")
35 if not isinstance(license_id, str) or not license_id.strip():
36 raise ValueError("manifest.license must be a non-empty string")
37
38 sections = raw.get("sections")
39 if not isinstance(sections, list) or not sections:
40 raise ValueError("manifest.sections must be a non-empty list")
41 section_ids: list[str] = []
42 for item in sections:
43 if not isinstance(item, dict) or "id" not in item:
44 raise ValueError("each section must be a mapping with id")
45 section_ids.append(str(item["id"]))
46
47 personas = raw.get("personas")
48 if not isinstance(personas, list) or not personas:
49 raise ValueError("manifest.personas must be a non-empty list")
50 persona_ids: list[str] = []
51 for item in personas:
52 if not isinstance(item, dict) or "id" not in item:
53 raise ValueError("each persona must be a mapping with id")
54 persona_ids.append(str(item["id"]))
55 status = item.get("status")
56 if status not in raw.get("status_badges", []):
57 raise ValueError(f"persona {item['id']} has unknown status {status!r}")
58
59 badges = raw.get("status_badges")
60 if not isinstance(badges, list) or not badges:
61 raise ValueError("manifest.status_badges must be a non-empty list")
62
63 funnel = raw.get("funnel_steps")
64 if not isinstance(funnel, list) or not funnel:
65 raise ValueError("manifest.funnel_steps must be a non-empty list")
66
67 return LandingManifest(
68 version=int(version),
69 license=license_id.strip(),
70 section_ids=tuple(section_ids),
71 persona_ids=tuple(persona_ids),
72 status_badges=frozenset(str(b) for b in badges),
73 funnel_steps=tuple(str(s) for s in funnel),
74 )
File History 1 commit
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 63 days ago