diff --git a/.claude/skills/time/SKILL.md b/.claude/skills/time/SKILL.md index d675afb..5d91865 100644 --- a/.claude/skills/time/SKILL.md +++ b/.claude/skills/time/SKILL.md @@ -114,6 +114,7 @@ Only when the human says so: `gh variable set TIME_MODE --body live`. The daily ## Ventures (optional) `gh variable set TIME_VENTURE --body on` starts the venture loop (`venture.yml`, every 20 minutes): one venture card a day, from the repos trending on GitHub to a launch page on GitHub Pages. Seats pitch, the board votes, board seats write launch plans that one seat synthesizes into `LAUNCH.md`, the worker builds the product as the page on `gh-pages` (the tool fills the first screen), deterministic checks and headless Chrome gate it, and seven days of signups decide (v2, kill, or one more week). It needs `VENICE_API_KEY`; the loop creates the `gh-pages` branch, the Pages site and the `signup` label if it can. +- Start one now, whatever the day: `gh workflow run venture.yml -f start=true` (or Actions → venture → Run workflow → "Start a new venture now"). A second card on the same day is titled `Venture (2)`. - Signups come in through the issue form `.github/ISSUE_TEMPLATE/early-access.yml` (label `signup`); the loop thanks each one on its issue. A signup is a distinct GitHub account (not the owner, not a bot) that opened an early-access issue or 👍'd the venture's signal issue. - Settings: `TIME_VENTURE_PITCH`, `TIME_VENTURE_BOARD` (comma-separated seats), `TIME_VENTURE_CHAIR` (synthesizes the launch plans), `TIME_VENTURE_MEASURE_DAYS` (default 7). - A page that still fails its checks after two fix rounds gets `needs-human`: fix it on `gh-pages` and comment `/time retry` on the card. A Venice outage only makes a stage wait; it never closes a card. diff --git a/.github/time/test_time.py b/.github/time/test_time.py index 9314dc3..05dad8c 100644 --- a/.github/time/test_time.py +++ b/.github/time/test_time.py @@ -828,3 +828,18 @@ assert any("the product starts 285px down at 390x844 (the header alone is 262px assert vt.normalize_plan({"pricing": [{"tier": "Free", "price": "$0"}], "primary_cta": "Export GIF"}, {})["primary_cta"] == "Join early access" assert vt.normalize_plan({"pricing": [{"tier": "Free", "price": "$0"}], "primary_cta": "Get early access"}, {})["primary_cta"] == "Get early access" print("ALL OK 27") + +# 36 a venture on demand: Run workflow with "start" makes a new card even when today's has run; the schedule doesn't +W = VWorld() +vt._build_on_pages = Worker() +W.add(f"Venture {vt.today()}", labels=["venture"], state="closed") +vtick({"TIME_VENTURE": "on", "TIME_VENTURE_HEAVY_START_MIN": "0"}) +assert [i["title"] for i in W.issues.values() if "venture" in i["labels"]] == [f"Venture {vt.today()}"] +vtick({"TIME_VENTURE": "on", "TIME_VENTURE_START": "1", "TIME_VENTURE_HEAVY_START_MIN": "0"}) +os.environ.pop("TIME_VENTURE_START", None); os.environ.pop("TIME_VENTURE_HEAVY_START_MIN", None) +titles36 = sorted(i["title"] for i in W.issues.values() if "venture" in i["labels"]) +assert titles36 == [f"Venture {vt.today()}", f"Venture {vt.today()} (2)"], titles36 +c36 = next(i["number"] for i in W.issues.values() if i["title"].endswith("(2)")) +assert W.beats(c36)[:2] == ["scouted", "pitched"], W.beats(c36) +assert vt.next_title({f"Venture {vt.today()}", f"Venture {vt.today()} (2)"}) == f"Venture {vt.today()} (3)" +print("ALL OK 28") diff --git a/.github/time/venture.py b/.github/time/venture.py index 1519c75..784099f 100644 --- a/.github/time/venture.py +++ b/.github/time/venture.py @@ -1048,6 +1048,14 @@ CARD_BODY = ("The venture loop runs this card by itself: scout the repos trendin "DONE: a launched venture linked from this card, or a digest saying where it stuck.") +def next_title(titles: set[str]) -> str: + """Today's card, or the next free 'Venture (n)' when today's has already run and another is asked for.""" + t, k = f"Venture {today()}", 2 + while t in titles: + t, k = f"Venture {today()} ({k})", k + 1 + return t + + def new_card(title: str) -> dict: return tm.gh("POST", f"/repos/{REPO}/issues", {"title": title, "body": CARD_BODY, "labels": ["venture"]}) @@ -1359,8 +1367,10 @@ def tick() -> int: refresh_daily(reg) except Exception as e: # noqa: BLE001 chores never block the cards print("chores failed:", e) - if env("TIME_VENTURE", "off") == "on" and not any(c["title"] == f"Venture {today()}" for c in venture_cards("all")): - new_card(f"Venture {today()}") + if env("TIME_VENTURE", "off") == "on": + titles = {c["title"] for c in venture_cards("all")} + if f"Venture {today()}" not in titles or env("TIME_VENTURE_START", "").lower() in ("1", "true", "yes"): + new_card(next_title(titles)) # once a day on the schedule; any time someone asks for one heavy_left, skip = 1, set() while time.monotonic() - t0 < budget: due = [] diff --git a/.github/workflows/venture.yml b/.github/workflows/venture.yml index 4aace60..f3c8e22 100644 --- a/.github/workflows/venture.yml +++ b/.github/workflows/venture.yml @@ -1,11 +1,17 @@ name: venture # The venture loop: trending repos -> pitches -> board -> launch plan -> launch page on Pages -> 7 days of signups. -# Every 20 minutes it advances whatever is due (cheap when nothing is); a new venture card starts once a day. +# Every 20 minutes it advances whatever is due (cheap when nothing is); a new venture card starts once a day, +# or right away: Actions -> venture -> Run workflow -> tick "Start a new venture now" (gh workflow run venture.yml -f start=true). # Off until TIME_VENTURE=on. Signups come in through .github/ISSUE_TEMPLATE/early-access.yml. on: schedule: - cron: "*/20 * * * *" workflow_dispatch: + inputs: + start: + description: "Start a new venture now, even if today's has already run" + type: boolean + default: false permissions: contents: write issues: write @@ -28,6 +34,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} VENICE_API_KEY: ${{ secrets.VENICE_API_KEY }} TIME_VENTURE: ${{ vars.TIME_VENTURE }} + TIME_VENTURE_START: ${{ inputs.start && '1' || '' }} TIME_VENTURE_PITCH: ${{ vars.TIME_VENTURE_PITCH }} TIME_VENTURE_BOARD: ${{ vars.TIME_VENTURE_BOARD }} TIME_VENTURE_CHAIR: ${{ vars.TIME_VENTURE_CHAIR }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 31f982e..9379bba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Added - Venture loop launches products, not pages: a launch round where board seats write plans and one synthesizes `LAUNCH.md` with attribution; launch pages where the working product fills the first screen under a slim header with one call to action, then pricing and honest limits, in a look that follows the product; early-access signups through a GitHub issue form, thanked on their issues; deterministic and headless-Chrome checks with fix rounds and an andon; a 7-day decision against success and kill lines; a studio index at `/ventures/`. +- Start a venture whenever you like: Actions → venture → Run workflow → "Start a new venture now" (`gh workflow run venture.yml -f start=true`); the schedule still starts one a day. - The board's taste: a delight anyone gets within a minute ranks above another developer utility, and when a product makes something people read (a deck, a card), its preloaded sample is the product's own pitch. - The studio index at `/ventures/` wears the Time site's blueprint theme (title block, grid paper, type), with each launched venture's laptop screenshot. - The headless-Chrome check fails a launch page where something sits on top of the product (a page fixed to the screen's height lets the next section slide over it). diff --git a/README.md b/README.md index 469ce23..401b63e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A GitHub-native agent factory. GitHub is the ledger, GitHub-hosted runners are t - **Heartbeat** (`heartbeat.yml`, hourly and whenever a card is labelled `ready`): moves cards forward. In `dry-run` (default) it only says what it would do; with `TIME_MODE=live` it dispatches each card to the worker (`TIME_WORKER`: a Venice seat such as `venice:kimi`, which edits the repo and opens the PR itself, or a Cursor cloud agent), which opens a PR that closes the card. An optional reviewer (`TIME_REVIEW`) checks the PR. At most `TIME_DAILY_CAP` launches a day. - **Mentions** (`mention.yml`): `@kimi`, `@grok 4.7`, `@qwen max high` in any issue, PR or comment gets that seat's reply (Venice). - **Hands-off** (`TIME_AUTOMERGE=on`): label an issue `spec` and Time splits it into cards that run one at a time. A card's PR merges itself once the reviewer approves and checks are green, then the next card starts. Objections, red checks and protected paths (workflows, deploy, secrets) go to a human. -- **Venture loop** (`venture.yml`, every 20 min once `TIME_VENTURE=on`): one venture card a day. It scouts the repos that gained the most stars this week; seats pitch; the board votes (Borda, quorum of two); every board seat writes a launch plan (positioning, first users, pricing, channels, 7-day success and kill lines) and one seat synthesizes them into `LAUNCH.md`; the worker builds the product as the page on `gh-pages` (the tool fills the first screen under a slim header with the one signup button; pricing, limits and the why sit below); deterministic checks and headless Chrome (phone and desktop) gate it, with two fix rounds before an andon; GitHub Pages ships it at `/ventures//`. Signups go through an issue form (`.github/ISSUE_TEMPLATE/early-access.yml`), each one thanked on its issue; after 7 days the count of distinct people decides: build v2, kill, or watch a week more. The studio index lives at `/ventures/`. Launch posts are drafts for a human; nothing is posted outside the repo. +- **Venture loop** (`venture.yml`, every 20 min once `TIME_VENTURE=on`): one venture card a day, or one right away from Actions → venture → Run workflow → "Start a new venture now". It scouts the repos that gained the most stars this week; seats pitch; the board votes (Borda, quorum of two); every board seat writes a launch plan (positioning, first users, pricing, channels, 7-day success and kill lines) and one seat synthesizes them into `LAUNCH.md`; the worker builds the product as the page on `gh-pages` (the tool fills the first screen under a slim header with the one signup button; pricing, limits and the why sit below); deterministic checks and headless Chrome (phone and desktop) gate it, with two fix rounds before an andon; GitHub Pages ships it at `/ventures//`. Signups go through an issue form (`.github/ISSUE_TEMPLATE/early-access.yml`), each one thanked on its issue; after 7 days the count of distinct people decides: build v2, kill, or watch a week more. The studio index lives at `/ventures/`. Launch posts are drafts for a human; nothing is posted outside the repo. - **Doctor** (`doctor.yml`, manual): runs the tests and checks label, settings and keys. - **Update** (`update.yml`, weekly or manual): opens a PR with the latest engine from teahubxyz/time, so every project stays on one Time instead of drifting copies.