diff --git a/.github/time/test_time.py b/.github/time/test_time.py index 53c342a..2adbfbe 100644 --- a/.github/time/test_time.py +++ b/.github/time/test_time.py @@ -278,6 +278,17 @@ votes = {"grok": '{"ranking": ["other", "other", "star-checker"], "reason": "r"} vt.tm.think = lambda seat, model, effort, system, user: votes[seat] w, meta = vt.board(ps, ["grok", "kimi", "qwen"]) assert w == "other" and meta["score"] == {"star-checker": 5, "other": 6}, meta +# no quorum: only one seat wants the winner -> None +votes = {"grok": '{"ranking": ["other"], "reason": "r"}', "kimi": '{"ranking": [], "reason": "none worth it"}', "qwen": '{"ranking": [], "reason": "pass"}'} +vt.tm.think = lambda seat, model, effort, system, user: votes[seat] +assert vt.board(ps, ["grok", "kimi", "qwen"])[0] is None +# a seat that answers in prose first gets re-asked +calls = {"n": 0} +def flaky(seat, model, effort, system, user): + calls["n"] += 1 + return "let me think..." if calls["n"] == 1 else answers["qwen"] +vt.tm.think = flaky +assert [p["slug"] for p in vt.pitch(repos, ["qwen"])] == ["star-checker"] and calls["n"] == 2 print("ALL OK 6") vt.tm.think = lambda *a: 'Sure! Here you go:\n```json\n{"slug": "x", "title": "T", "one_liner": "o", "why_now": "w", "page": "p {with braces}"}\n```' assert vt.ask_json("q", "s", "u")["page"] == "p {with braces}" diff --git a/.github/time/venture.py b/.github/time/venture.py index 95eabc6..97d5978 100644 --- a/.github/time/venture.py +++ b/.github/time/venture.py @@ -54,17 +54,23 @@ most stars on GitHub this week. They tell you what builders care about right now {radar} -Pitch ONE small product the studio could ship as a single static web page today: a tool, calculator, explainer, -directory, checker, or playground that rides the same wave. It must be useful on its own, need no backend, and be -honest (no fake data, no fake testimonials). Reply with ONLY JSON: -{{"slug": "kebab-case", "title": "...", "one_liner": "...", "why_now": "which repo(s) and trend this rides", "page": "what the page does, concretely, in 3-5 sentences"}}""" +Pitch ONE venture the studio could launch today as a single static web page: a tool, calculator, explainer, directory, +checker or playground that rides the same wave. It must work on its own with no backend, be honest (no fake data, +no fake testimonials), and be a *venture*, not just a tool: it has a name, a specific person it is for, a reason to +come back, and a next version worth paying for. Reply with ONLY JSON: +{{"slug": "kebab-case", "name": "a brand name, one or two words", "title": "...", "one_liner": "...", + "for_whom": "the specific person this is for, in one sentence", "why_now": "which repo(s) and trend this rides", + "page": "what the free page does, concretely, in 3-5 sentences", + "next": "what the paid or bigger version would be, in one sentence", + "cta": "the one thing a visitor should do to say they want more (vote, star, join a list, request a feature)"}}""" VOTE = """You are `{seat}`, voting on the board of an agent-run venture studio. Pitches: {pitches} -Rank your top three by: would a real person use this page this week, can one agent build it as a static page in an -hour, is it honest. Reply with ONLY JSON: {{"ranking": ["slug1", "slug2", "slug3"], "reason": "one sentence"}}""" +Rank up to three by: would the named person use this page this week, can one agent build it as a static page in an +hour, is there a believable next version someone would pay for, is it honest. If none is worth a day of the studio, +rank nothing. Reply with ONLY JSON: {{"ranking": ["slug1", "slug2"], "reason": "one sentence"}} (ranking may be []).""" def ask_json(seat: str, system: str, user: str) -> dict | None: @@ -92,7 +98,11 @@ def ask_json(seat: str, system: str, user: str) -> dict | None: def pitch(repos: list[dict], seats: list[str]) -> list[dict]: out = [] for s in seats: - p = ask_json(s, f"You are `{s}`.", PITCH.format(seat=s, radar=radar(repos))) + p = None + for _ in range(2): # a seat that answers in prose gets one more go + p = ask_json(s, f"You are `{s}`. Reply with JSON only.", PITCH.format(seat=s, radar=radar(repos))) + if p and p.get("slug") and p.get("title"): + break if p and p.get("slug") and p.get("title"): p["slug"] = re.sub(r"[^a-z0-9-]", "-", p["slug"].lower())[:40].strip("-") p["by"] = s @@ -115,24 +125,46 @@ def board(pitches: list[dict], seats: list[str]) -> tuple[str | None, dict]: for pts, slug in zip((3, 2, 1), seen): score[slug] += pts if not any(score.values()): - return None, votes - return max(score, key=lambda k: (score[k], -[p["slug"] for p in pitches].index(k))), {"votes": votes, "score": score} + return None, {"votes": votes, "score": score} + winner = max(score, key=lambda k: (score[k], -[p["slug"] for p in pitches].index(k))) + if sum(1 for v in votes.values() if winner in v.get("ranking", [])) < 2: + return None, {"votes": votes, "score": score} # no quorum: at least two seats must want it + return winner, {"votes": votes, "score": score} # ---------- build and ship ---------- -BUILD_BODY = """Build the venture **{title}** as one static page. - -{page} +BUILD_BODY = """Build the venture **{name}** ({title}) as one static page. +For: {for_whom} +What the free page does: {page} Why now: {why_now} +Next version (not built today, but the page should point at it): {next} Write `ventures/{slug}/index.html`: a complete, self-contained page (inline CSS and JS, no build step, no external -calls except optional public CDNs). Make it genuinely usable, mobile-friendly, and honest: no invented numbers, -reviews or logos. Add a one-line footer "built by the Time venture loop" linking to the repo. Also add a row for it to -`ventures/index.html` (create that index if missing: a plain list of ventures with title, one-liner and link). +calls except optional public CDNs). Genuinely usable, mobile-friendly, honest: no invented numbers, reviews or logos. +Brand it as **{name}** (title, header, page