Files
playbook/antigravity-awesome-skills/skills/find-complementary-founders/scripts/match_profiles.py
T
2026-07-29 07:59:17 +00:00

252 lines
8.1 KiB
Python

#!/usr/bin/env python3
"""Rank eligible own-owner thread submissions by complementarity and alignment."""
from __future__ import annotations
import argparse
import glob
import importlib.util
import json
import sys
from pathlib import Path
LEVEL_VALUE = {
"unknown": 0.0,
"observed": 0.2,
"practiced": 0.5,
"strong": 0.8,
"standout": 1.0,
}
CONFIDENCE_VALUE = {
"none": 0.0,
"low": 0.35,
"medium": 0.7,
"high": 1.0,
}
class MatchError(ValueError):
"""Raised for an invalid public profile."""
_PROFILE_VALIDATOR = None
def profile_validator():
global _PROFILE_VALIDATOR
if _PROFILE_VALIDATOR is not None:
return _PROFILE_VALIDATOR
path = Path(__file__).with_name("validate_profile.py")
spec = importlib.util.spec_from_file_location("findmate_profile_validator", path)
if spec is None or spec.loader is None:
raise MatchError("Cannot load the FindMate public-profile validator")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
_PROFILE_VALIDATOR = module
return module
def load_profile(path: Path) -> dict:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise MatchError(f"Cannot load {path}: {exc}") from exc
if not isinstance(value, dict):
raise MatchError(f"{path} must contain a JSON object")
validate_profile(value, path)
value["_source_path"] = str(path.resolve())
return value
def validate_profile(profile: dict, path: Path) -> None:
validator = profile_validator()
try:
validator.validate_profile(profile)
except validator.ValidationError as exc:
raise MatchError(f"{path} failed public-profile validation: {exc}") from exc
def normalized_strings(values: object) -> set[str]:
if not isinstance(values, list):
return set()
return {str(value).strip().casefold() for value in values if str(value).strip()}
def overlap_score(left: object, right: object) -> float:
left_set = normalized_strings(left)
right_set = normalized_strings(right)
if not left_set or not right_set:
return 0.0
return len(left_set & right_set) / len(left_set | right_set)
def contribution_value(profile: dict, section: str, dimension: str) -> float:
entry = profile.get(section, {}).get(dimension, {})
level = LEVEL_VALUE.get(entry.get("level"), 0.0)
confidence = CONFIDENCE_VALUE.get(entry.get("confidence"), 0.0)
return level * (0.5 + 0.5 * confidence)
def requested_coverage(owner: dict, candidate: dict) -> tuple[float, list[str]]:
seeking = owner.get("seeking", {})
checks: list[float] = []
reasons: list[str] = []
for dimension in seeking.get("stages", []):
value = contribution_value(candidate, "stage_contributions", dimension)
checks.append(value)
if value >= 0.5:
reasons.append(f"covers stage gap: {dimension}")
for dimension in seeking.get("functions", []):
value = contribution_value(candidate, "functional_contributions", dimension)
checks.append(value)
if value >= 0.5:
reasons.append(f"covers capability gap: {dimension}")
return (sum(checks) / len(checks) if checks else 0.0), reasons
def reciprocal_coverage(owner: dict, candidate: dict) -> float:
seeking = candidate.get("seeking", {})
checks: list[float] = []
for dimension in seeking.get("stages", []):
checks.append(contribution_value(owner, "stage_contributions", dimension))
for dimension in seeking.get("functions", []):
checks.append(contribution_value(owner, "functional_contributions", dimension))
return sum(checks) / len(checks) if checks else 0.0
def evidence_quality(candidate: dict) -> float:
entries = list(candidate.get("stage_contributions", {}).values())
entries += list(candidate.get("functional_contributions", {}).values())
relevant = [entry for entry in entries if entry.get("level") != "unknown"]
if not relevant:
return 0.0
confidence = sum(
CONFIDENCE_VALUE.get(entry.get("confidence"), 0.0) for entry in relevant
) / len(relevant)
proof_bonus = min(len(candidate.get("public_evidence", [])) / 3, 1.0)
return 0.75 * confidence + 0.25 * proof_bonus
def score_match(owner: dict, candidate: dict) -> dict:
coverage, reasons = requested_coverage(owner, candidate)
reciprocal = reciprocal_coverage(owner, candidate)
owner_seek = owner.get("seeking", {})
candidate_seek = candidate.get("seeking", {})
themes = overlap_score(
owner_seek.get("project_themes"), candidate_seek.get("project_themes")
)
principles = overlap_score(
owner_seek.get("shared_principles"), candidate_seek.get("shared_principles")
)
modes = overlap_score(
owner_seek.get("collaboration_modes"),
candidate_seek.get("collaboration_modes"),
)
alignment = 0.4 * themes + 0.35 * principles + 0.25 * modes
evidence = evidence_quality(candidate)
final = round(
100
* (0.50 * coverage + 0.15 * reciprocal + 0.25 * alignment + 0.10 * evidence),
1,
)
if themes > 0:
reasons.append("shares project themes")
if principles > 0:
reasons.append("shares operating principles")
if modes > 0:
reasons.append("shares collaboration mode")
return {
"alias": candidate["alias"],
"score": final,
"reasons": reasons,
"contact": candidate["contact"],
"profile_source": candidate["_source_path"],
"components": {
"gap_coverage": round(coverage, 3),
"reciprocal_coverage": round(reciprocal, 3),
"alignment": round(alignment, 3),
"evidence_quality": round(evidence, 3),
},
"review_required": [
"verify public evidence",
"discuss commitment and decision rights",
"obtain both humans' consent before direct contact",
],
}
def expand_candidate_paths(patterns: list[str]) -> list[Path]:
paths: list[Path] = []
for pattern in patterns:
matches = [Path(item) for item in glob.glob(pattern)]
if not matches and Path(pattern).is_file():
matches = [Path(pattern)]
for match in matches:
if match not in paths:
paths.append(match)
return paths
def exclude_owner_source(owner: dict, candidates: list[dict]) -> list[dict]:
owner_source = owner.get("_source_path")
if not isinstance(owner_source, str):
raise MatchError("Owner profile lacks source identity")
return [
candidate
for candidate in candidates
if candidate.get("_source_path") != owner_source
]
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Rank owner-approved profiles obtained from marked FindMate thread "
"submissions by capability gaps and alignment."
)
)
parser.add_argument("owner", type=Path)
parser.add_argument(
"--candidate",
action="append",
required=True,
help="Candidate file or glob; repeat as needed",
)
parser.add_argument("--limit", type=int, default=10)
return parser.parse_args()
def main() -> int:
args = parse_args()
try:
owner = load_profile(args.owner)
candidates = [
load_profile(path) for path in expand_candidate_paths(args.candidate)
]
if not candidates:
raise MatchError("No candidate profiles found")
results = [
score_match(owner, candidate)
for candidate in exclude_owner_source(owner, candidates)
]
results.sort(key=lambda item: item["score"], reverse=True)
output = {
"owner_alias": owner["alias"],
"method": "heuristic shortlist; not a compatibility verdict",
"matches": results[: max(1, args.limit)],
}
json.dump(output, sys.stdout, indent=2, ensure_ascii=False)
sys.stdout.write("\n")
except MatchError as exc:
print(f"error: {exc}", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
raise SystemExit(main())