sync: secret names Gitea accepts (SYNC_GITHUB_KEY, SYNC_GITHUB_TOKEN) (#179)
sync: secret names Gitea accepts (SYNC_GITHUB_KEY, SYNC_GITHUB_TOKEN) (#179) Gitea refuses secret names starting GITHUB_ or GITEA_, so the GitHub copy's deploy key is in SYNC_GITHUB_KEY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017K4vyY3yUD3GsvoZaAtKkD
This commit was merged in pull request #179.
This commit is contained in:
1 parent
e34994bbc6
commit
745231844f
4 files changed
+10
-9
No files matched your search
@@ -16,11 +16,11 @@ cloud session's branch on the copy), and a pull mirror makes the receiving repo
|
||||
|
||||
time/sync.json:
|
||||
{"repos": [{"repo": "teahubxyz/time", "out": ["main"],
|
||||
"copies": [{"name": "github", "url": "https://github.com/teahubxyz/time.git",
|
||||
"token": "GITHUB_MIRROR_TOKEN", "import": ["claude/*"]}]}]}
|
||||
"copies": [{"name": "github", "url": "git@github.com:teahubxyz/time.git",
|
||||
"ssh_key": "SYNC_GITHUB_KEY", "import": ["claude/*"]}]}]}
|
||||
|
||||
`token` names a secret (an environment variable the workflow passes), never the token itself; a copy whose token isn't
|
||||
set is skipped with a note. Or `ssh_key` names a secret holding an SSH private key (a GitHub deploy key: write access
|
||||
`token` names a secret (an environment variable the workflow passes; Gitea refuses secret names starting GITHUB_ or
|
||||
GITEA_), never the token itself; a copy whose token isn't set is skipped with a note. Or `ssh_key` names a secret holding an SSH private key (a GitHub deploy key: write access
|
||||
to that one repo and nothing else) for an `ssh://` or `git@host:` URL; the host's keys are pinned (github.com's are
|
||||
built in, others go in `known_hosts`), never learned on first use. The same script syncs Gitea with Gitea: it runs on the home, so a pair the home can't
|
||||
reach (hb-5's local Gitea from prod) runs from the side that can reach both, with that side's own sync.json.
|
||||
|
||||
@@ -34,5 +34,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TIME_TOKEN: ${{ secrets.TIME_TOKEN }} # branches and PRs on the home go in as the `time` user
|
||||
SYNC_FORCE: ${{ inputs.force && '1' || '' }}
|
||||
GITHUB_MIRROR_KEY: ${{ secrets.GITHUB_MIRROR_KEY }} # github.com/teahubxyz/time: a deploy key with write access
|
||||
GITHUB_MIRROR_TOKEN: ${{ secrets.GITHUB_MIRROR_TOKEN }} # or a token, for a copy over https
|
||||
# Gitea refuses secret names starting GITHUB_ or GITEA_, hence SYNC_
|
||||
SYNC_GITHUB_KEY: ${{ secrets.SYNC_GITHUB_KEY }} # github.com/teahubxyz/time: a deploy key with write access
|
||||
SYNC_GITHUB_TOKEN: ${{ secrets.SYNC_GITHUB_TOKEN }} # or a token, for a copy over https
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
- The second fix round escalates to a different model from the first round: `claude:opus` with the Claude seats (it was `venice:kimi code`, and grok 4.7 before that). `TIME_VENTURE_ESCALATE` overrides it.
|
||||
|
||||
### Added
|
||||
- Sync (`sync.py`, `time/sync.json`, the `sync` workflow): a repo's copies on other forges follow its home, under one rule, one home per repo and one writer per branch. The home's `main` (the `out` branches) goes to every copy fast-forward only; a copy with commits of its own is reported in a `needs-human` issue and never overwritten, unless the `force` dispatch input replaces its history once. A copy's branches matching its `import` patterns (`claude/*` from cloud sessions) are the copy's to write: they come back to the home under the same name with a PR, commit messages in the body (so a `Closes #N` adopts the card), and Time's council reviews and lands them; a branch already in `main`, or one whose PR was closed at the same commit, is left alone. Gitea's own mirrors don't fit: a push mirror force-pushes every branch (it would delete the copy's work branches) and a pull mirror makes the home read-only. Tokens are secrets named in the file, scrubbed from every message. The first copy is `github.com/teahubxyz/time`, so cloud sessions clone a current engine; it is skipped until `GITHUB_MIRROR_TOKEN` is set, and GitHub Actions must be off on it. The same script syncs Gitea with Gitea, run from whichever side can reach both.
|
||||
- Sync (`sync.py`, `time/sync.json`, the `sync` workflow): a repo's copies on other forges follow its home, under one rule, one home per repo and one writer per branch. The home's `main` (the `out` branches) goes to every copy fast-forward only; a copy with commits of its own is reported in a `needs-human` issue and never overwritten, unless the `force` dispatch input replaces its history once. A copy's branches matching its `import` patterns (`claude/*` from cloud sessions) are the copy's to write: they come back to the home under the same name with a PR, commit messages in the body (so a `Closes #N` adopts the card), and Time's council reviews and lands them; a branch already in `main`, or one whose PR was closed at the same commit, is left alone. Gitea's own mirrors don't fit: a push mirror force-pushes every branch (it would delete the copy's work branches) and a pull mirror makes the home read-only. Tokens are secrets named in the file, scrubbed from every message; a copy can instead use an SSH deploy key (`ssh_key`: write access to that one repo and nothing else), with the host's keys pinned, never learned on first use. Gitea refuses secret names starting `GITHUB_` or `GITEA_`, so the names start `SYNC_`. The first copy is `github.com/teahubxyz/time`, so cloud sessions clone a current engine; it pushes with the deploy key in `SYNC_GITHUB_KEY`, and GitHub Actions is off on it. The same script syncs Gitea with Gitea, run from whichever side can reach both.
|
||||
- Adaptive budget: spend is paced to each capacity pool instead of one fixed daily cap. `time/pools.json` (kept by the operator) lists the pools: `expiring` credit allows `(balance - spent since as_of) / days to resets_at x TIME_PACE` a day (default 1.0), so a quiet day raises tomorrow's share; a `window` pool (`weekly:mon`, `hours:5`) allows `window_usd` per window and refills at its reset; a `balance` pool keeps `TIME_DAILY_USD`. A call is checked against the pool its provider books to, and `TIME_DAILY_USD` is the fallback for a route with no pool; the monthly cap stays global. Ledger rows carry a `pool` field, `doctor` prints each pool's balance, spend, next reset and today's allowance, and Astra's log line reports spend per pool. It ships with an empty `pools.json`, so nothing changes until the operator fills it in. `pools.json` is read through the API once per run, like the ledger, since every workflow checks out only `.github/time`.
|
||||
- The router (time#68, #72; `router.py`, `time/router.json`): the factory picks its own next work across ventures, features, refinements and optimizations. Astra's seat no longer picks cards in prose; it judges every candidate (each open card with a `DONE:` line that isn't a parent, a human's or the venture loop's own, plus "start a new venture") with a kind, a value 1–5 and a size S/M/L, and code ranks them: `weight[kind] × restless(kind) × age × (value + 0.5 × cards waiting on it) / size`. The weights are the owner's dial per kind (all 1, balanced); a kind not picked lately climbs (`restless`, 0.05 a day for up to 10 days) so none starves; an L is ranked but never queued (split it first). The Astra log shows the ranked table. `mode: dry` posts it and touches nothing; `live` queues from the top while the day's money left covers each card's estimated cost (`cost_usd`), writes the seat's new cards unqueued (candidates the next day), and the heartbeat's `promote_next` promotes queued cards in the live ranking's order instead of oldest first. `off`, or no file, is the planner as before. It starts `dry`. The policy is one function, so a bandit policy (Beth, Jev, Fable's maths) can replace it. Astra also reads the last 14 days of its log instead of the thread's first twenty comments.
|
||||
- A signup's Venture field matches the venture's slug, name, home folder or an old slug, ignoring case, spaces and punctuation. The field is typed by a person (Teahub does not prefill it from the link), who writes "Cartridge" or "Loot Drop", not `box-art-night`; those signups were not counted.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"_about": "Copies of repos on other forges, kept in step by .github/time/sync.py (the sync workflow). Each repo has one home (this forge) and every branch one writer: the `out` branches go from the home to each copy, fast-forward only; a copy's branches matching its `import` patterns come back to the home as PRs. `token` (https) or `ssh_key` (a deploy key, for ssh/git@ URLs; host keys pinned) names the secret that holds the credential, never the credential itself; a copy whose secret isn't set is skipped. A GitHub copy needs GitHub Actions turned off, since these workflows must only run here.",
|
||||
"_about": "Copies of repos on other forges, kept in step by .github/time/sync.py (the sync workflow). Each repo has one home (this forge) and every branch one writer: the `out` branches go from the home to each copy, fast-forward only; a copy's branches matching its `import` patterns come back to the home as PRs. `token` (https) or `ssh_key` (a deploy key, for ssh/git@ URLs; host keys pinned) names the secret that holds the credential, never the credential itself (Gitea refuses secret names starting GITHUB_ or GITEA_); a copy whose secret isn't set is skipped. A GitHub copy needs GitHub Actions turned off, since these workflows must only run here.",
|
||||
"repos": [
|
||||
{
|
||||
"repo": "teahubxyz/time",
|
||||
@@ -10,7 +10,7 @@
|
||||
{
|
||||
"name": "github",
|
||||
"url": "git@github.com:teahubxyz/time.git",
|
||||
"ssh_key": "GITHUB_MIRROR_KEY",
|
||||
"ssh_key": "SYNC_GITHUB_KEY",
|
||||
"import": [
|
||||
"claude/*"
|
||||
]
|
||||
|
||||
Reference in new issue
Block a user