#!/usr/bin/env python3 """feature-control flag connector (ATLAS-WP-0003-T04). Inventory feature-control keys and emit `feature-flag` surfaces that LINK to the authoritative feature-control key (`sources[].role: feature-control-key`) and contain no evaluation logic (PRD FR-12 delegation boundary). Read-only. Source resolution (first available): 1. --keys : newline- or yaml-list of feature keys 2. ~/feature-control/registry/indexes/feature-keys.yaml (if present) Degrades gracefully when feature-control has no key registry yet (planning phase). Usage: python3 tools/connector_featurecontrol.py [--keys keys.yaml] make connect-featurecontrol """ from __future__ import annotations import sys from pathlib import Path try: import yaml except ImportError as exc: # pragma: no cover raise SystemExit(f"setup error: missing PyYAML ({exc})") from connector_base import run_connector FC_KEYS = Path.home() / "feature-control" / "registry" / "indexes" / "feature-keys.yaml" def _load_keys(keys_file: str | None) -> list[str]: src = Path(keys_file) if keys_file else FC_KEYS if not src.exists(): print(f"feature-control: no key registry at {src} (planning phase — none yet)") return [] raw = src.read_text() try: data = yaml.safe_load(raw) except yaml.YAMLError: data = None if isinstance(data, dict): keys = data.get("keys") or data.get("feature_keys") or [] elif isinstance(data, list): keys = data else: keys = [ln.strip() for ln in raw.splitlines() if ln.strip() and not ln.startswith("#")] return [str(k) for k in keys] def keys_to_candidates(keys: list[str]) -> list[tuple[dict, str]]: out: list[tuple[dict, str]] = [] for key in keys: slug = key.replace(".", "-").replace("_", "-").lower() sid = f"surface.infotech.feature-control.{slug}" entry = { "id": sid, "name": f"feature flag: {key}", "kind": "feature-flag", "summary": f"Runtime feature availability controlled by feature-control key `{key}`.", "owner": "feature-control", "scope": {"allowed_layers": ["company", "environment", "tenant", "user"], "default_layer": "company"}, "mutability": "hot-reloadable", "security_class": "operational", "sources": [{"repo": "feature-control", "endpoint": f"openfeature:{key}", "role": "feature-control-key"}], "relations": {"related_to": []}, } out.append((entry, f"Links to feature-control key `{key}`. config-atlas maps " f"the flag; feature-control owns evaluation. Promote or reject.\n")) return out def main(argv: list[str]) -> int: keys_file = None if "--keys" in argv: i = argv.index("--keys") keys_file = argv[i + 1] if i + 1 < len(argv) else None return run_connector("feature-control", keys_to_candidates(_load_keys(keys_file))) if __name__ == "__main__": sys.path.insert(0, str(Path(__file__).resolve().parent)) raise SystemExit(main(sys.argv[1:]))