# AGENTS.md — Teahub v1 (Green Tea) You are on **prod**: https://teahub.xyz, Gitea 28 on Fly (app `green-tea`, configured by `gitea/fly.toml`, its look in `gitea/custom`). Everything here is the factory's own record; nothing here is scaffolding from before 2026-10-14. ``` you (any agent) ──MCP──▶ mcp.teahub.xyz ──as the signed-in user──▶ teahub.xyz (Gitea) │ login: GitHub (now), nostr (next) ├── teahubxyz/teahub the spine (this repo) ├── teahubxyz/time Time: the clock (Gitea Actions on the Fly runner) └── Time = the automation layer. It lives here, on prod (teahubxyz/time); the GitHub copy is an archived proof of concept. Time acts as the Gitea user `time` (its own token, TIME_TOKEN) for every write except comments and commit statuses, which stay with the Actions identity (`gitea-actions`); code goes in as git pushes. Seats post as themselves: a review by `haiku` is a Gitea review by the user `haiku` (one Gitea user per model line, TIME_SEAT_). Rhythm: Astra (daily, 13:13 UTC) is the router: its seat judges every candidate card (kind, value, size) and `router.py` ranks them with the owner's weights per kind in teahubxyz/time's `time/router.json` (mode dry: it posts the ranking on the Astra log; live: it queues from the top and the heartbeat promotes in ranked order). The heartbeat (hourly, and on demand) builds `ready` cards with a seat, gets each PR reviewed by the council, and merges on approve + green CI, engine code included. Only the workflow directories (`.github/workflows/`) stay a human's merge: the CEO batches them. A card whose PR already exists (Codex, a chat, the CEO) is adopted: labelled `ready`, it is reviewed and landed, never rebuilt. Housekeeping sweeps daily (13:17). Ventures: paused since 2026-10-19 (TIME_VENTURE=off; the launched ones are still measured) until the router starts them (time#170). When on: one a day, built for quality; the day's card starts once every other venture card is launched or held. Opus leads alone: a research round over the trending repos and the studio's track record, one pick, the plan, the build (`claude:opus`) and the judging (a 1-10 score: under 8 earns polish rounds, then it launches with its score); sonnet drafts the launch posts. No fable. Batches (off) keep the board. Each venture lives at .yolopreneur.lol (gh-pages `ventures//`); the portfolio at yolopreneur.lol is gh-pages `ventures/index.html`, made by hand, and reads `ventures.json`. Seat workers (and the venture builder) carry Ponytail's rules (time `.github/time/rules/ponytail.md`); reviewers don't. Tiers: Luna = haiku (Codex: luna), Terra = sonnet (terra), Sol = opus (sol), Astra = fable (astra), the C level. Venice seats (kimi, grok, ...) break ties. The operator hears from the Astra tier only on exception or check-in. Spend: every model call is booked to time/ledger/.jsonl, cached tokens priced. TIME_MONTHLY_USD=100 is the owner's envelope; TIME_DAILY_USD (15) paces it and gates launches; a run launched within budget finishes (1.5x). A breach opens an `andon` issue. No model decides whether to spend. hb-5 (the Mac) is NOT part of prod: it is the operator's playground and the old instance (matcha.teahub.xyz). Boring (ops MCP) lives on the Mac and may reach prod as a client; prod never depends on it. ``` ## Moves - Read first: this file, `docs/decisions.md` (why things are the way they are), `docs/rebuild.md` (what it took). - Work is a card: an issue with a `DONE:` line. Open it, do it, receipt it on the issue, close it. `queued` runs it when its turn comes (oldest first, `Depends on #N` respected); `ready` runs it on the next beat; `spec` has Time split it. - A card is what a Terra worker finishes in forty turns. Larger work is a parent labelled `planned` with children that carry their own `DONE:` lines; a parent is never `queued`. Three cards died at the turn ceiling before this rule. - A card says where it lives. `Base: ` (every venture card: `Base: gh-pages`) makes the worker cut its branch from there and open the PR against it; without the line, the default branch. `PR: #N` names a PR that already answers the card, so Time reviews it instead of building again. - Before a task, find its route in `SKILLS.md` and load only the skills it names, by path. No model decides which skill to load. - You act as yourself (Time too: it is the `time` user). There is no shared service token on prod. If you can't do something, your Gitea permissions are the reason; ask the owner (`teahubxyz`) on the issue. - Porting from the old instance: only when a card needs it, only after reading it, one piece per commit, and a line in `docs/rebuild.md` saying what it took. - Secrets: prod holds Gitea's own keys, the GitHub OAuth client, the MCP's OAuth client, and the seat keys listed in `docs/decisions.md`. Nothing else. Do not add one without a decision there. A temporary admin token is deleted (sqlite3 on the Fly box) and the deletion checked before you write that it's gone. - Be boring by default; get clever only on an ANDON. Deterministic scripts before cheap inference before expensive inference. ## Situations What to do when you meet one of these. Each row names the procedure; the procedure names the skill or the code. | You see | Do | |---|---| | A `ready` card with no worker yet, and a PR already answers it | Nothing: Time adopts the PR on its next beat (a `pr` beat with `adopted: true`). Make sure the PR body says `Closes #N` or the card says `PR: #N`. | | The reviewer objected; the PR is `needs-human` | Read the objection on the PR. If it is right and small, comment `/time retry` on the card: the worker gets the review text and answers on the same branch. If it is wrong, say so on the PR as yourself (Approve/Object comments from trusted users count) and remove the label. | | A PR is approved but `mergeable: false` | It fell behind its base. Until time#123 lands, a human re-cuts the branch from the base and re-applies the change (or merges the base into the branch); a `Closes #N` line keeps the card's adoption. | | A worker stopped at the turn ceiling ("stopped without a PR after N steps") | First check the card's `Base:` line (a venture card without `Base: gh-pages` cannot find its files). Then split the card; never retry the same card unchanged twice. | | An open venture card whose history is comments by a person, not `gitea-actions` (the GitHub-era cards, migrated) | Close it. Time counts only its own beats, so the loop takes such a card as unstarted and pitches a new venture on it (batch time#26 was reused that way on 2026-10-18). The venture itself stays in `ventures.json`. | | An `andon` issue: spend cap reached | Read the ledger for the day. The daily cap is pacing (the CEO may raise it with the owner's OK; log it in `docs/decisions.md`); the monthly cap is the owner's alone. Close the andon once handled; Astra queues nothing while one is open. | | A PR head shows `heartbeat / tick: failure` | Not a check. Time's own workflow statuses (heartbeat, mention, venture, astra, doctor, update, steward) are ignored by the merge gate (time#124). CI decides. | | Housekeeping posted a stale list on the Astra log | Follow `.agents/skills/housekeeping/SKILL.md` in teahubxyz/time: decide each `needs-human`, idle PR, parked or `error` card. Housekeeping never acts on the list itself. | | A workflow file needs to change | Open the PR, label it `needs-human`, and note it on the Astra log. The CEO merges workflow files in a batch; nobody else, and never through a side channel. | | A card touches money or a human-only step (a purchase, a signature, a Whop upload) | Stop at the step, write what is ready and what is needed on the card, and escalate to the owner. Everything up to that step still gets done. | | You want a new skill, or a vendored one changed | `SKILLS.md` → Moves. One PR with the skill, its katas and a ledger line. | ## Where things are - MCP source: `mcp/` (FastMCP, deploy with `fly deploy`). - Decisions: `docs/decisions.md` (append-only, dated). - Rebuild log: `docs/rebuild.md`. - Skills: `SKILLS.md` (routes, ledger, belts); the vendored folders are in `.agents/skills/`. - Time's engine: `teahubxyz/time` `.github/time/` (`time.py` the clock, `venture.py` the venture loop, `astra.py` the daily planner, `router.py` its ranking, `housekeeping.py` the daily sweep, `sync.py` copies on other forges (`time/sync.json`: one home, one writer per branch; main out fast-forward, `claude/*` branches home as PRs), `seats.json` the seats). Spend pools (expiring credit, refilling windows) are `time/pools.json`. Its SOPs: `.agents/skills//SKILL.md` in that repo. The planner, the steward and housekeeping all write to the "Astra log" issue there (time#57).