Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/orgs/Pick-Your-AI/mark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 58 additions & 1 deletion references/brand.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,61 @@
"title": "#F1F5F9"
}
},
"pickyourai": {
"badge_accent": "115E59",
"badge_brand": "0F766E",
"canvas": {
"color": "#042F2E",
"grid": "#14B8A6",
"grid_opacity": 0.07,
"kind": "flat-grid"
},
"company_label": "Pick Your AI",
"eyebrow_case": "upper",
"measured": false,
"tokens": {
"accent": "#14B8A6",
"accent_deep": "#115E59",
"accent_mid": "#14B8A6",
"bar": {
"from": "#115E59",
"height": 6,
"to": "#14B8A6"
},
"body": "#CCFBF1",
"card_edge": "#CCFBF1",
"card_fill": "#F0FDFA",
"card_ink": "#042F2E",
"card_shadow": {
"blur": 12,
"color": "#000000",
"dx": 6,
"dy": 8,
"opacity": 0.35
},
"chip_attr": {
"bg": "#115E59",
"fg": "#F0FDFA"
},
"chip_key": {
"bg": "#FBBF24",
"fg": "#042F2E"
},
"edge": {
"color": "#14B8A6",
"opacity": 0.28
},
"eyebrow": "#5EEAD4",
"gold_trace": "#FBBF24",
"icon_ink": "#0F766E",
"icon_spark": "#B45309",
"ink": "#042F2E",
"muted": "#99F6E4",
"plate_low": "#042F2E",
"subtitle": "#CCFBF1",
"title": "#F0FDFA"
}
},
"romgenie": {
"canvas": {
"color": "#002C31",
Expand Down Expand Up @@ -783,10 +838,12 @@
"description": null,
"display_name": "Pick Your AI",
"eyebrow": null,
"family": "completetech",
"family": "pickyourai",
"family_topics": [],
"footer": false,
"former_slugs": [],
"hub_url": "https://pickyour.ai",
"mark": "assets/orgs/Pick-Your-AI/mark.png",
"nav_name": "Pick Your AI",
"tier": "T3"
},
Expand Down
6 changes: 6 additions & 0 deletions references/brand.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@
"company_label": {
"type": "string"
},
"badge_brand": {
"type": "string"
},
"badge_accent": {
"type": "string"
},
"eyebrow_case": {
"enum": [
"upper",
Expand Down
35 changes: 34 additions & 1 deletion references/brand.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,37 @@ families:
signal_core: "#FFE0E5"
icon_ink: "#3A1A33"
icon_spark: "#6B214F" # deep magenta: readable on pink
pickyourai:
# Pick Your AI's own palette, from Pick-Your-AI/.github assets/PROMPTS.md and build_assets.py (teal surfaces, one amber
# accent, deep teal ground). Not CompleteTech branding. Unmeasured: the layout has no Pick Your AI golden yet.
measured: false
company_label: Pick Your AI
badge_brand: "0F766E" # README badges (shields.io hex, no #): teal, not CompleteTech blue
badge_accent: "115E59"
eyebrow_case: upper
canvas: {kind: flat-grid, color: "#042F2E", grid: "#14B8A6", grid_opacity: 0.07}
tokens:
eyebrow: "#5EEAD4"
title: "#F0FDFA"
subtitle: "#CCFBF1"
body: "#CCFBF1"
muted: "#99F6E4"
ink: "#042F2E"
plate_low: "#042F2E"
card_fill: "#F0FDFA" # light icon card, as CompleteTech: the icon prompt and QA assume dark ink on a light plate
card_ink: "#042F2E"
card_edge: "#CCFBF1"
card_shadow: {dx: 6, dy: 8, blur: 12, color: "#000000", opacity: 0.35}
chip_key: {bg: "#FBBF24", fg: "#042F2E"}
chip_attr: {bg: "#115E59", fg: "#F0FDFA"}
bar: {from: "#115E59", to: "#14B8A6", height: 6}
edge: {color: "#14B8A6", opacity: 0.28}
gold_trace: "#FBBF24"
accent: "#14B8A6"
accent_mid: "#14B8A6"
accent_deep: "#115E59"
icon_ink: "#0F766E"
icon_spark: "#B45309" # deep amber: readable on the light icon card (the chip and accent stay #FBBF24)
libreevolve:
measured: false
company_label: LibreEvolve
Expand Down Expand Up @@ -618,7 +649,7 @@ orgs:
family_topics: []
footer: true
Pick-Your-AI:
family: completetech
family: pickyourai
former_slugs: []
tier: T3
display_name: Pick Your AI
Expand All @@ -627,6 +658,8 @@ orgs:
eyebrow: null
brand_topic: pick-your-ai
family_topics: []
mark: assets/orgs/Pick-Your-AI/mark.png
hub_url: https://pickyour.ai
footer: false
Jevelopers-neatHack-2026:
family: completetech
Expand Down
17 changes: 16 additions & 1 deletion scripts/ctshowcase/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ def canonical(obj) -> str:
return json.dumps(obj, sort_keys=True, ensure_ascii=False, separators=(",", ":"))


_T3_SIGNED_OFF = False


def set_t3_signed_off(value: bool) -> None:
"""Record the run's `--allow-t3`: set once by the CLI, read by every owner gate (check_owner, art_status,
footer_status). Off by default, so a T3 owner is refused unless the caller passes the flag after an approved
t3-metadata or t3-signoff row."""
global _T3_SIGNED_OFF
_T3_SIGNED_OFF = bool(value)


def t3_signed_off() -> bool:
return _T3_SIGNED_OFF


class Brand:
"""Merged view over brand.yaml."""

Expand Down Expand Up @@ -96,7 +111,7 @@ def check_owner(self, owner: str, *, allow_t3: bool = False) -> str | None:
raise Refused(f"{owner} is an excluded owner; every command refuses", log="exempt")
if status == "hold":
raise Refused(f"{owner} is on HOLD", log="hold")
if status == "t3" and not allow_t3:
if status == "t3" and not (allow_t3 or t3_signed_off()):
raise Refused(f"{owner} is T3; needs co-owner sign-off", log="t3-signoff")
return status

Expand Down
6 changes: 3 additions & 3 deletions scripts/ctshowcase/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from pathlib import Path

from .config import Brand, load_repo_override, override_from_manifests
from .config import Brand, load_repo_override, override_from_manifests, t3_signed_off
from .detect import detect
from .errors import ConfigError, NeedsInput
from .facts import RepoFacts
Expand Down Expand Up @@ -150,7 +150,7 @@ def art_status(self) -> dict:
no_card = "exempt:no-card" if self.card_exempt() else None # §5.4 holds whatever the owner code says
if watching: # a watching-org repository that is not a fork and not its `.github`: decided explicitly
return {"banner": watching, "card": no_card or watching, "icon": watching}
if status in ("t3", "hold", "excluded"):
if status in ("t3", "hold", "excluded") and not (status == "t3" and t3_signed_off()):
code = {"t3": "t3-signoff", "hold": "hold", "excluded": "exempt"}[status]
return {"banner": code, "card": no_card or code, "icon": code}
if no_card:
Expand Down Expand Up @@ -182,7 +182,7 @@ def footer_status(self) -> str | None:
"""None when the decision 18 footer applies, else why not: `exempt:watching-fork`, `hold:watching-non-fork`,
`t3-signoff`, `hold` (org row `footer: hold`) or `exempt:footer` (org row `footer: false`). The watching org's
`footer: false` exempts only its forks (org.md), so its `.github` needs the footer."""
if self.brand.owner_status(self.owner) == "t3":
if self.brand.owner_status(self.owner) == "t3" and not t3_signed_off():
return "t3-signoff"
watching = self.watching_status()
if watching or self.brand.owner_status(self.owner) == "watching":
Expand Down
3 changes: 2 additions & 1 deletion scripts/ctshowcase/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from . import describe, footer as footermod, labels as labelsmod, pnglint, readmelint, svglint, topics as topicsmod
from . import readme as readmemod
from .config import t3_signed_off
from .context import RepoContext
from .detect import standard

Expand Down Expand Up @@ -88,7 +89,7 @@ def row(ctx: RepoContext) -> dict:
logs += rl.get("logs", [])
else:
cells["R7"] = "unknown" if not rd else "fail"
if ctx.brand.owner_status(ctx.owner) == "t3":
if ctx.brand.owner_status(ctx.owner) == "t3" and not t3_signed_off():
cells["R7"] = "hold" # T3: no README change before co-owner sign-off (lint readme refuses)
lic = ctx.meta.get("license")
cells["R8"] = "pass" if lic or not ctx.public else "warn"
Expand Down
25 changes: 14 additions & 11 deletions scripts/ctshowcase/org.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,11 @@ def _star_entry(estate: Estate, org: str, reg: dict) -> dict | None:
return sl if sl.get("name") else None


def _star_held(estate: Estate, org: str) -> str | None:
def _star_held(estate: Estate, org: str, allow_t3: bool = False) -> str | None:
from .config import t3_signed_off
st = estate.resolver.owner_status(org)
if st == "t3" and (allow_t3 or t3_signed_off()):
st = None # co-owner sign-off lifts the T3 hold (decision 12); HOLD and excluded stay held
if st in ("t3", "hold", "excluded"):
return st
if estate.resolver.family_hold(org):
Expand Down Expand Up @@ -1523,7 +1526,7 @@ def star_list_plan(estate: Estate, org: str, *, lists_capture: dict | None = Non
login = r.canonical(org)
reg = r.registry_org(org)
entry = _star_entry(estate, org, reg)
held = _star_held(estate, org)
held = _star_held(estate, org, allow_t3)
base = {"org": login, "reads": [{"name": "star-lists", "argv": read_command("star_lists")}], "jobs": [], "pending": [],
"logs": []}
if not entry:
Expand Down Expand Up @@ -2226,7 +2229,7 @@ def register(sub) -> None:
s.add_argument("--render", action="store_true", help="profile: also render the cards, badges and org banner into --out-dir")
s.add_argument("--banner", help="profile: hero path relative to profile/ (default assets/banner.jpg, or assets/intro.svg)")
s.add_argument("--email-approved", action="store_true", default=None, help="the org email (q26) is approved")
s.add_argument("--allow-t3", action="store_true", help="T3 org after co-owner sign-off")
s.add_argument("--allow-t3", action="store_true", default=argparse.SUPPRESS, help="T3 org after co-owner sign-off")
s.add_argument("--allow-unmeasured", action="store_true")
s.add_argument("--lists", help="pins/profile: star_lists.graphql capture (SOC-01)")
s.add_argument("--repo-ids", help="pins: JSON {owner/repo: node id} from repo_id.graphql reads")
Expand Down Expand Up @@ -2290,30 +2293,30 @@ def cmd_org(args):
estate = estate_from_args(args)
act = args.action
if act == "profile" and args.user:
res, code = user_profile(estate, args.user, out_dir=args.out_dir, render=args.render, allow_t3=args.allow_t3,
res, code = user_profile(estate, args.user, out_dir=args.out_dir, render=args.render, allow_t3=getattr(args, "allow_t3", False),
allow_unmeasured=args.allow_unmeasured, meta_dir=args.meta_dir)
elif act == "profile":
res, code = profile(estate, org, out_dir=args.out_dir, render=args.render, banner=args.banner,
email_approved=args.email_approved, allow_t3=args.allow_t3,
email_approved=args.email_approved, allow_t3=getattr(args, "allow_t3", False),
allow_unmeasured=args.allow_unmeasured, lists_capture=_json_file(args.lists),
meta_dir=args.meta_dir)
elif act == "about":
res, code = about(estate, org, email_approved=args.email_approved, allow_t3=args.allow_t3)
res, code = about(estate, org, email_approved=args.email_approved, allow_t3=getattr(args, "allow_t3", False))
elif act == "pins":
res, code = pins(estate, org, allow_t3=args.allow_t3, lists_capture=_json_file(args.lists),
res, code = pins(estate, org, allow_t3=getattr(args, "allow_t3", False), lists_capture=_json_file(args.lists),
repo_ids=_json_file(args.repo_ids))
res["jobs"] = res["jobs"] + res["star_list"].get("jobs", [])
elif act == "labels":
if args.repos_from_gh:
capture_label_repos(estate, org, allow_t3=args.allow_t3)
res, code = labels_plan(estate, org, allow_t3=args.allow_t3, checkouts=args.checkouts, dot_github=args.dot_github,
capture_label_repos(estate, org, allow_t3=getattr(args, "allow_t3", False))
res, code = labels_plan(estate, org, allow_t3=getattr(args, "allow_t3", False), checkouts=args.checkouts, dot_github=args.dot_github,
registry_free=args.repos_from_gh)
elif act == "health":
res, code = health(estate, org, repo_dir=args.dot_github, allow_t3=args.allow_t3)
res, code = health(estate, org, repo_dir=args.dot_github, allow_t3=getattr(args, "allow_t3", False))
else:
snap = _json_file(args.snapshot) or {}
row = doctor(estate, org, snap, repo_dir=args.dot_github, avatar=args.avatar, email_approved=args.email_approved,
allow_t3=args.allow_t3)
allow_t3=getattr(args, "allow_t3", False))
if args.org_doctor_format != "json":
print(as_tsv(row, args.header) if args.org_doctor_format == "tsv" else as_md(row, args.header))
return None, OK
Expand Down
7 changes: 4 additions & 3 deletions scripts/ctshowcase/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""
from __future__ import annotations

import argparse
import hashlib
import json
import re
Expand Down Expand Up @@ -1153,7 +1154,7 @@ def register(sub) -> None:
s.add_argument("--approved", help="decisions/approved.tsv")
s.add_argument("--approved-yaml", help="decisions/APPROVED_ACTIONS.yaml (approved org renames and merges, CQ-03)")
s.add_argument("--actions", help="decisions/estate-actions.json (the old and new names of approved org renames)")
s.add_argument("--allow-t3", action="store_true")
s.add_argument("--allow-t3", action="store_true", default=argparse.SUPPRESS)
s.add_argument("--no-status-update", action="store_true")
s.add_argument("--files-dir", help="copy-plan: write the REST view bodies under DIR (paths as in `files`)")
s.add_argument("--out", help="normalize: write the template spec JSON here; readme: write the README here")
Expand Down Expand Up @@ -1239,15 +1240,15 @@ def cmd_project(args):
copied = _load(args.copied)
if act == "estate-plan":
res = estate_plan(r, source=args.source, copied=copied, user_id=args.user_id, approval_id=args.approval_id,
allow_t3=args.allow_t3, reviewed=args.reviewed)
allow_t3=getattr(args, "allow_t3", False), reviewed=args.reviewed)
else:
if not args.target:
raise ShowcaseError("copy-plan needs --target ORG (or @me)", log="usage")
ws = _load(args.workstreams)
res = copy_plan(args.target, role=args.role, source=args.source, title=args.title, display_name=args.display_name,
copied=copied, workstreams=ws, repositories=[x for x in (args.repos or "").split(",") if x] or None,
approval_id=args.approval_id, resolver=r, user_id=args.user_id, out_of_scope=args.out_of_scope,
reviewed=args.reviewed, status_update=not args.no_status_update, allow_t3=args.allow_t3)
reviewed=args.reviewed, status_update=not args.no_status_update, allow_t3=getattr(args, "allow_t3", False))
if args.jobs and res.get("files") and not args.files_dir:
# each REST view job reads its body with --input; the writer's guard refuses a body it cannot read
raise ShowcaseError("copy-plan writes REST view bodies: pass --files-dir DIR with --jobs so each view job's --input "
Expand Down
14 changes: 11 additions & 3 deletions scripts/ctshowcase/readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ def banner_html(ctx: RepoContext) -> str:

def badges(ctx: RepoContext) -> list[tuple[str, str, str | None]]:
"""[(alt, image url, link)] per §7.2: language, type, license, CI. 2-4."""
b = ctx.brand.raw["badges"]
b = dict(ctx.brand.raw["badges"])
for k in ("brand", "accent"): # a family outside CompleteTech carries its own badge colours (brand.yaml badge_brand, badge_accent)
if ctx.style.get(f"badge_{k}"):
b[k] = ctx.style[f"badge_{k}"]
jev = ctx.style["name"] == "jevplay-games"
extra = "?labelColor=1E1E1E" if jev else ""
out = []
Expand Down Expand Up @@ -296,11 +299,14 @@ def license_section(ctx: RepoContext) -> tuple[str, str] | None:
if not year:
ctx.log("needs-input:license-year")
return None
return "License", rv["private"].replace("<year>", year)
private = rv["private"].replace("<year>", year)
if ctx.org.get("family") not in (None, "completetech"):
private = private.replace("CompleteTech LLC", ctx.org.get("display_name") or ctx.owner)
return "License", private
if lic == "MIT":
if f.exists("BRAND_ASSETS.md"):
return "License", rv["skills_mit"]
if ctx.tier.startswith("T2"):
if ctx.tier.startswith("T2") or ctx.org.get("family") not in (None, "completetech"):
return "License", rv["t2_mit"].replace("<Brand>", ctx.org.get("display_name") or ctx.owner)
return "License", rv["t1_mit"]
if lic and lic not in ("NOASSERTION",):
Expand Down Expand Up @@ -343,6 +349,8 @@ def section_facts(ctx: RepoContext, h2: str) -> str | None:
if hp and ctx.meta.get("homepage_status") == 200:
return f"**[{re.sub(r'^https?://', '', hp).rstrip('/')}]({hp})**"
if h2 == "Ownership":
if ctx.org.get("family") not in (None, "completetech"):
return f"Organization: {ctx.owner}"
return f"Owner: @CompleteDotTech · Organization: {ctx.owner}"
if h2 == "Files" and ctx.det["variant"] == "J":
rows = [("profile/README.md", f"Organization profile shown at github.com/{ctx.owner}")]
Expand Down
3 changes: 2 additions & 1 deletion scripts/ctshowcase/readmelint.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ def lint(ctx: RepoContext, path: Path, variant: str | None = None) -> dict:
warns.append(f"badges-short: {nbadges} badge (no licence or language to show yet)")
for s in shields:
col = re.split(r"[?)]", s.rsplit("-", 1)[-1])[0].upper()
if re.fullmatch(r"[0-9A-F]{6}", col) and col not in PALETTE:
family_cols = {str(ctx.style.get(k) or "").upper() for k in ("badge_brand", "badge_accent")}
if re.fullmatch(r"[0-9A-F]{6}", col) and col not in PALETTE and col not in family_cols:
fails.append(f"R7 off-palette badge colour {col}")
# tagline == description
desc = (ctx.meta.get("description") or "").strip()
Expand Down
3 changes: 2 additions & 1 deletion scripts/ctshowcase/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,14 @@ def owner_status(self, login: str) -> str | None:

def check_owner(self, login: str, *, allow_t3: bool = False, allow_hold: bool = False) -> str | None:
"""Refuse excluded, hold and (unless signed off) T3 orgs under any of their logins; return the status."""
from .config import t3_signed_off
from .errors import Refused
st = self.owner_status(login)
if st == "excluded":
raise Refused(f"{login} is an excluded owner; every command refuses", log="exempt")
if st == "hold" and not allow_hold:
raise Refused(f"{login} is on HOLD", log="hold")
if st == "t3" and not allow_t3:
if st == "t3" and not (allow_t3 or t3_signed_off()):
raise Refused(f"{login} is T3; needs co-owner sign-off", log="t3-signoff")
return st

Expand Down
Loading
Loading