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

533 lines
18 KiB
Python

#!/usr/bin/env python3
"""Create private evidence scores and a privacy-minimized public profile."""
from __future__ import annotations
import argparse
import json
import os
import re
import sys
from collections import defaultdict
from datetime import date, datetime, timezone
from pathlib import Path
from urllib.parse import urlparse
SCHEMA_VERSION = "1.0"
STAGES = (
"zero_to_one",
"one_to_ten",
"ten_to_hundred",
)
FUNCTIONS = (
"problem_discovery",
"product",
"engineering",
"design",
"go_to_market",
"operations",
"people_leadership",
"capital_partnerships",
)
EVIDENCE_WEIGHTS = {
"customer_outcome": 5,
"operational_outcome": 5,
"shipped_artifact": 4,
"repeated_responsibility": 4,
"peer_feedback": 2,
"preference": 1,
}
HIGH_QUALITY_KINDS = {
"customer_outcome",
"operational_outcome",
"shipped_artifact",
"repeated_responsibility",
}
SENSITIVE_PATTERNS = {
"email address": re.compile(
r"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b", re.IGNORECASE
),
"phone-like number": re.compile(r"(?<!\w)(?:\+?\d[\s().-]*){9,}(?!\w)"),
"IP address": re.compile(
r"\b(?:25[0-5]|2[0-4]\d|1?\d?\d)"
r"(?:\.(?:25[0-5]|2[0-4]\d|1?\d?\d)){3}\b"
),
"secret-like text": re.compile(
r"(?:api[_ -]?key|password|passwd|private[_ -]?key|"
r"authorization:\s*bearer|moltbook_[A-Za-z0-9_-]{8,}|"
r"gh[opusr]_[A-Za-z0-9_]{12,})",
re.IGNORECASE,
),
"local filesystem path": re.compile(r"(?:/Users/|/home/|[A-Z]:\\Users\\)"),
}
class ProfileError(ValueError):
"""Raised for invalid or unsafe profile input."""
def load_json(path: Path) -> dict:
try:
data = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError as exc:
raise ProfileError(f"Input file not found: {path}") from exc
except json.JSONDecodeError as exc:
raise ProfileError(f"Invalid JSON in {path}: {exc}") from exc
if not isinstance(data, dict):
raise ProfileError("Profile input must be a JSON object")
return data
def require_string(value: object, field: str, *, maximum: int = 500) -> str:
if not isinstance(value, str) or not value.strip():
raise ProfileError(f"{field} must be a non-empty string")
clean = value.strip()
if len(clean) > maximum:
raise ProfileError(f"{field} exceeds {maximum} characters")
return clean
def validate_public_text(value: str, field: str) -> str:
for label, pattern in SENSITIVE_PATTERNS.items():
if pattern.search(value):
raise ProfileError(f"{field} appears to contain a {label}")
return value
def validate_alias(value: object) -> str:
alias = require_string(value, "alias", maximum=50)
if not re.fullmatch(r"[a-zA-Z0-9][a-zA-Z0-9_-]{1,49}", alias):
raise ProfileError(
"alias must be a 2-50 character pseudonym using letters, digits, _ or -"
)
return alias
def validate_url(value: object, field: str, *, contact: bool = False) -> str:
url = require_string(value, field, maximum=500)
parsed = urlparse(url)
if parsed.scheme != "https" or not parsed.hostname or parsed.username:
raise ProfileError(f"{field} must be a credential-free HTTPS URL")
if parsed.query or parsed.fragment:
raise ProfileError(f"{field} must not contain a query string or fragment")
if contact:
if parsed.hostname.lower() != "github.com":
raise ProfileError(f"{field} must use github.com")
parts = [part for part in parsed.path.split("/") if part]
if len(parts) < 3 or parts[2] not in {"issues", "discussions"}:
raise ProfileError(
f"{field} must point to a GitHub issues or discussions page"
)
return url
def validate_dimension_list(
values: object, field: str, allowed: tuple[str, ...]
) -> list[str]:
if values is None:
return []
if not isinstance(values, list):
raise ProfileError(f"{field} must be a list")
result: list[str] = []
for value in values:
if value not in allowed:
raise ProfileError(
f"{field} contains unsupported value {value!r}; "
f"allowed: {', '.join(allowed)}"
)
if value not in result:
result.append(value)
return result
def validate_string_list(values: object, field: str, *, limit: int = 10) -> list[str]:
if values is None:
return []
if not isinstance(values, list) or len(values) > limit:
raise ProfileError(f"{field} must be a list with at most {limit} items")
result: list[str] = []
for index, value in enumerate(values):
item = require_string(value, f"{field}[{index}]", maximum=80)
validate_public_text(item, f"{field}[{index}]")
if item not in result:
result.append(item)
return result
def parse_iso_date(value: object, field: str) -> date:
text = require_string(value, field, maximum=10)
try:
return date.fromisoformat(text)
except ValueError as exc:
raise ProfileError(f"{field} must use YYYY-MM-DD") from exc
def validate_consent(value: object) -> dict:
if not isinstance(value, dict):
raise ProfileError("consent must be an object")
if value.get("public_profile") is not True:
raise ProfileError(
"consent.public_profile must be true before generating a public profile"
)
approved = parse_iso_date(value.get("approved_at"), "consent.approved_at")
expires = parse_iso_date(value.get("expires_on"), "consent.expires_on")
today = datetime.now(timezone.utc).date()
if approved > today:
raise ProfileError("consent.approved_at must not be in the future")
if expires < today:
raise ProfileError("consent.expires_on is already past")
if expires < approved:
raise ProfileError("consent.expires_on must not precede approved_at")
if (expires - approved).days > 180:
raise ProfileError("public profile consent may cover at most 180 days")
scope = validate_public_text(
require_string(value.get("scope"), "consent.scope", maximum=180),
"consent.scope",
)
return {
"state": "public_profile_approved",
"approved_at": approved.isoformat(),
"expires_on": expires.isoformat(),
"scope": scope,
}
def score_label(score: int) -> str:
if score == 0:
return "unknown"
if score < 25:
return "observed"
if score < 50:
return "practiced"
if score < 75:
return "strong"
return "standout"
def confidence_label(evidence_count: int, strong_count: int) -> str:
if evidence_count >= 3 and strong_count >= 2:
return "high"
if evidence_count >= 2 and strong_count >= 1:
return "medium"
if evidence_count >= 1:
return "low"
return "none"
def validate_evidence(values: object) -> tuple[list[dict], list[dict]]:
if not isinstance(values, list) or not values:
raise ProfileError("evidence must be a non-empty list")
if len(values) > 50:
raise ProfileError("evidence may contain at most 50 items")
private_items: list[dict] = []
public_items: list[dict] = []
seen_ids: set[str] = set()
for index, raw in enumerate(values):
if not isinstance(raw, dict):
raise ProfileError(f"evidence[{index}] must be an object")
evidence_id = require_string(raw.get("id"), f"evidence[{index}].id", maximum=60)
if not re.fullmatch(r"[a-zA-Z0-9][a-zA-Z0-9_-]{0,59}", evidence_id):
raise ProfileError(f"evidence[{index}].id has invalid characters")
if evidence_id in seen_ids:
raise ProfileError(f"duplicate evidence id: {evidence_id}")
seen_ids.add(evidence_id)
kind = raw.get("kind")
if kind not in EVIDENCE_WEIGHTS:
raise ProfileError(
f"evidence[{index}].kind must be one of {', '.join(EVIDENCE_WEIGHTS)}"
)
stages = validate_dimension_list(
raw.get("stages"), f"evidence[{index}].stages", STAGES
)
functions = validate_dimension_list(
raw.get("functions"), f"evidence[{index}].functions", FUNCTIONS
)
if not stages and not functions:
raise ProfileError(
f"evidence[{index}] must tag at least one stage or function"
)
note = require_string(
raw.get("private_note"), f"evidence[{index}].private_note", maximum=1000
)
share = raw.get("share") is True
private_items.append(
{
"id": evidence_id,
"kind": kind,
"weight": EVIDENCE_WEIGHTS[kind],
"stages": stages,
"functions": functions,
"private_note": note,
"share": share,
}
)
if share:
claim = validate_public_text(
require_string(
raw.get("public_claim"),
f"evidence[{index}].public_claim",
maximum=180,
),
f"evidence[{index}].public_claim",
)
proof = validate_url(
raw.get("public_proof"), f"evidence[{index}].public_proof"
)
public_items.append(
{
"id": evidence_id,
"claim": claim,
"proof": proof,
"supports": stages + functions,
}
)
return private_items, public_items
def compute_vectors(evidence: list[dict], dimensions: tuple[str, ...]) -> dict:
raw_scores: defaultdict[str, int] = defaultdict(int)
counts: defaultdict[str, int] = defaultdict(int)
strong_counts: defaultdict[str, int] = defaultdict(int)
ids: defaultdict[str, list[str]] = defaultdict(list)
dimension_key = "stages" if dimensions == STAGES else "functions"
for item in evidence:
if item["kind"] == "preference":
continue
for dimension in item[dimension_key]:
raw_scores[dimension] += item["weight"]
counts[dimension] += 1
ids[dimension].append(item["id"])
if item["kind"] in HIGH_QUALITY_KINDS:
strong_counts[dimension] += 1
output: dict[str, dict] = {}
for dimension in dimensions:
score = min(100, round(raw_scores[dimension] / 15 * 100))
output[dimension] = {
"score": score,
"level": score_label(score),
"confidence": confidence_label(counts[dimension], strong_counts[dimension]),
"evidence_count": counts[dimension],
"evidence_ids": ids[dimension],
}
return output
def validate_preferences(value: object) -> dict:
if value is None:
value = {}
if not isinstance(value, dict):
raise ProfileError("preferences must be an object")
return {
"stages": validate_dimension_list(
value.get("stages"), "preferences.stages", STAGES
),
"functions": validate_dimension_list(
value.get("functions"), "preferences.functions", FUNCTIONS
),
}
def validate_seeking(value: object) -> dict:
if not isinstance(value, dict):
raise ProfileError("seeking must be an object")
stages = validate_dimension_list(value.get("stages"), "seeking.stages", STAGES)
functions = validate_dimension_list(
value.get("functions"), "seeking.functions", FUNCTIONS
)
if not stages and not functions:
raise ProfileError("seeking must name at least one stage or function")
return {
"stages": stages,
"functions": functions,
"project_themes": validate_string_list(
value.get("project_themes"), "seeking.project_themes"
),
"collaboration_modes": validate_string_list(
value.get("collaboration_modes"), "seeking.collaboration_modes", limit=5
),
"shared_principles": validate_string_list(
value.get("shared_principles"), "seeking.shared_principles"
),
}
def validate_contact(value: object) -> dict:
if not isinstance(value, dict):
raise ProfileError("public_contact must be an object")
contact_type = value.get("type")
if contact_type not in {"github_issues", "github_discussions"}:
raise ProfileError(
"public_contact.type must be github_issues or github_discussions"
)
return {
"type": contact_type,
"url": validate_url(value.get("url"), "public_contact.url", contact=True),
}
def public_vectors(vectors: dict) -> dict:
return {
name: {
"score": values["score"],
"level": values["level"],
"confidence": values["confidence"],
"evidence_count": values["evidence_count"],
}
for name, values in vectors.items()
}
def write_json(path: Path, data: dict, *, private: bool) -> None:
if private and not path.name.endswith(".private.json"):
raise ProfileError("private output filename must end in .private.json")
path.parent.mkdir(parents=True, exist_ok=True)
if path.is_symlink():
raise ProfileError(f"Refusing to write through symlink: {path}")
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC | getattr(os, "O_NOFOLLOW", 0)
fd = os.open(path, flags, 0o600 if private else 0o644)
with os.fdopen(fd, "w", encoding="utf-8") as handle:
os.fchmod(handle.fileno(), 0o600 if private else 0o644)
json.dump(data, handle, indent=2, ensure_ascii=False, sort_keys=True)
handle.write("\n")
def build_assessment_components(data: dict) -> dict:
alias = validate_alias(data.get("alias"))
summary = validate_public_text(
require_string(data.get("summary"), "summary", maximum=280), "summary"
)
evidence, public_evidence = validate_evidence(data.get("evidence"))
preferences = validate_preferences(data.get("preferences"))
seeking = validate_seeking(data.get("seeking"))
stage_vectors = compute_vectors(evidence, STAGES)
function_vectors = compute_vectors(evidence, FUNCTIONS)
generated_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
return {
"alias": alias,
"summary": summary,
"evidence": evidence,
"public_evidence": public_evidence,
"preferences": preferences,
"seeking": seeking,
"stage_vectors": stage_vectors,
"function_vectors": function_vectors,
"generated_at": generated_at,
}
def private_assessment_from_components(components: dict) -> dict:
return {
"schema_version": SCHEMA_VERSION,
"alias": components["alias"],
"generated_at": components["generated_at"],
"evidence": components["evidence"],
"stage_contributions": components["stage_vectors"],
"functional_contributions": components["function_vectors"],
"preferences": components["preferences"],
"seeking": components["seeking"],
"publication_state": "private_draft_only",
}
def build_private_assessment(data: dict) -> dict:
"""Build a private draft without requiring any publication consent."""
return private_assessment_from_components(build_assessment_components(data))
def build_profiles(data: dict) -> tuple[dict, dict]:
components = build_assessment_components(data)
contact = validate_contact(data.get("public_contact"))
consent = validate_consent(data.get("consent"))
public_profile = {
"schema_version": SCHEMA_VERSION,
"profile_type": "founder-collaboration",
"alias": components["alias"],
"summary": components["summary"],
"generated_at": components["generated_at"],
"expires_on": consent["expires_on"],
"stage_contributions": public_vectors(components["stage_vectors"]),
"functional_contributions": public_vectors(components["function_vectors"]),
"preferences": components["preferences"],
"seeking": components["seeking"],
"public_evidence": components["public_evidence"],
"contact": contact,
"consent": consent,
"interpretation": {
"status": "owner-approved collaboration hypothesis",
"not_for": [
"employment screening",
"psychometric diagnosis",
"sensitive-trait inference",
],
},
}
private_assessment = private_assessment_from_components(components)
private_assessment["publication_state"] = "public_profile_approved"
private_assessment["public_profile_preview"] = public_profile
return public_profile, private_assessment
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Score owner-selected evidence and produce a safe public profile."
)
parser.add_argument("input", type=Path, help="Private owner input JSON")
parser.add_argument(
"--public-output", type=Path, help="Write the public profile JSON here"
)
parser.add_argument(
"--private-output",
type=Path,
help="Optional private assessment; filename must end in .private.json",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
try:
data = load_json(args.input)
if args.private_output and not args.public_output:
private_assessment = build_private_assessment(data)
write_json(args.private_output, private_assessment, private=True)
return 0
public_profile, private_assessment = build_profiles(data)
if args.public_output:
write_json(args.public_output, public_profile, private=False)
if args.private_output:
write_json(args.private_output, private_assessment, private=True)
if not args.public_output:
json.dump(
public_profile,
sys.stdout,
indent=2,
ensure_ascii=False,
sort_keys=True,
)
sys.stdout.write("\n")
except ProfileError as exc:
print(f"error: {exc}", file=sys.stderr)
return 2
return 0
if __name__ == "__main__":
raise SystemExit(main())