From ef6c843de893f633e884fd07697ddabef1580c4b Mon Sep 17 00:00:00 2001 From: Chibuzor Date: Tue, 8 Sep 2026 14:28:31 +0400 Subject: [PATCH 1/7] team-init: point generated install instructions at the seamfix fork MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fork-local change. gstack-team-init appends a '## gstack' block to each adopting repo's CLAUDE.md whose install lines said github.com/garrytan/gstack — teammates following it would install upstream, bypassing the reviewed fork. Found by the Seamfix Way adoption dry run (2026-09-08). Re-apply on every upstream sync (see the ai-governance runbook). Co-Authored-By: Claude Fable 5.1 --- bin/gstack-team-init | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/gstack-team-init b/bin/gstack-team-init index 99538425f2..64b7f64b0a 100755 --- a/bin/gstack-team-init +++ b/bin/gstack-team-init @@ -54,11 +54,11 @@ fi if [ "$MODE" = "optional" ]; then SNIPPET='## gstack (recommended) -This project uses [gstack](https://github.com/garrytan/gstack) for AI-assisted workflows. +This project uses [gstack](https://github.com/seamfix/gstack) for AI-assisted workflows. Install it for the best experience: ```bash -git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack +git clone --depth 1 https://github.com/seamfix/gstack.git ~/.claude/skills/gstack cd ~/.claude/skills/gstack && ./setup --team ``` @@ -82,7 +82,7 @@ If GSTACK_MISSING: STOP. Do not proceed. Tell the user: > gstack is required for all AI-assisted work in this repo. > Install it: > ```bash -> git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack +> git clone --depth 1 https://github.com/seamfix/gstack.git ~/.claude/skills/gstack > cd ~/.claude/skills/gstack && ./setup --team > ``` > Then restart your AI coding tool. @@ -136,7 +136,7 @@ BLOCKED: gstack is not installed globally. gstack is required for AI-assisted work in this repo. Install it: - git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack + git clone --depth 1 https://github.com/seamfix/gstack.git ~/.claude/skills/gstack cd ~/.claude/skills/gstack && ./setup --team Then restart your AI coding tool. @@ -202,5 +202,5 @@ if [ ${#GENERATED[@]} -gt 0 ]; then fi echo "" echo "Each developer then runs:" -echo " git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack" +echo " git clone --depth 1 https://github.com/seamfix/gstack.git ~/.claude/skills/gstack" echo " cd ~/.claude/skills/gstack && ./setup --team" From a68d0074241aa379cb0b6cd7bddedb8b97f327b8 Mon Sep 17 00:00:00 2001 From: Chibuzor Date: Tue, 8 Sep 2026 16:44:58 +0400 Subject: [PATCH 2/7] fork-local: every install/upgrade path points at seamfix/gstack; update-check compares against the install's own origin; guard test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-ups from Diyan's review of #2: 1. bin/gstack-update-check derived the remote from a hardcoded garrytan URL, so every fork install was told UPGRADE_AVAILABLE 1.68.3 → 1.81.0 forever while auto-upgrade pulled the fork. Now derives owner/repo from the install's own origin (the pattern gstack-session-update already uses); falls back to seamfix/gstack. 2. gstack-upgrade/SKILL.md.tmpl (and the generated SKILL.md) cloned upstream over vendored installs — silently reverting the fork. Now clones the fork. 3. README (3 clone commands), CONTRIBUTING, tutorial: install commands point at the fork. Upstream authorship and doc pointers untouched. 4. test/team-mode.test.ts: a tripwire test — generated CLAUDE.md carries the fork URL, and none of the install/upgrade files reference github.com/garrytan/gstack. Fails loudly after an upstream sync until re-applied. TODOS.md: fork-local section (enable the fork's CI workflows; the re-apply list). Co-Authored-By: Claude Fable 5.1 --- CONTRIBUTING.md | 2 +- README.md | 6 +++--- TODOS.md | 16 ++++++++++++++++ bin/gstack-update-check | 11 ++++++++--- docs/tutorial-document-generate.md | 2 +- gstack-upgrade/SKILL.md | 2 +- gstack-upgrade/SKILL.md.tmpl | 2 +- test/team-mode.test.ts | 14 ++++++++++++++ 8 files changed, 45 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 77e188ae8a..006efaf9ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ gstack skills are Markdown files that Claude Code discovers from a `skills/` dir That's what dev mode does. It symlinks your repo into the local `.claude/skills/` directory so Claude Code reads skills straight from your checkout. ```bash -git clone https://github.com/garrytan/gstack.git && cd gstack +git clone https://github.com/seamfix/gstack.git && cd gstack bun install # install dependencies bin/dev-setup # activate dev mode ``` diff --git a/README.md b/README.md index 7b2b9305ea..3830ea7bc7 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Fork it. Improve it. Make it yours. And if you want to hate on free open source Open Claude Code and paste this. Claude does the rest. -> Install gstack: run **`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`** then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /setup-gbrain, /retro, /investigate, /document-release, /document-generate, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn. Then ask the user if they also want to add gstack to the current project so teammates get it. +> Install gstack: run **`git clone --single-branch --depth 1 https://github.com/seamfix/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`** then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /setup-gbrain, /retro, /investigate, /document-release, /document-generate, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn. Then ask the user if they also want to add gstack to the current project so teammates get it. ### Step 2: Team mode — auto-update for shared repos (recommended) @@ -67,7 +67,7 @@ Swap `required` for `optional` if you'd rather nudge teammates than block them. OpenClaw spawns Claude Code sessions via ACP, so every gstack skill just works when Claude Code has gstack installed. Paste this to your OpenClaw agent: -> Install gstack: run `git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` to install gstack for Claude Code. Then add a "Coding Tasks" section to AGENTS.md that says: when spawning Claude Code sessions for coding work, tell the session to use gstack skills. Include these examples — security audit: "Load gstack. Run /cso", code review: "Load gstack. Run /review", QA test a URL: "Load gstack. Run /qa https://...", build a feature end-to-end: "Load gstack. Run /autoplan, implement the plan, then run /ship", plan before building: "Load gstack. Run /office-hours then /autoplan. Save the plan, don't implement." +> Install gstack: run `git clone --single-branch --depth 1 https://github.com/seamfix/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` to install gstack for Claude Code. Then add a "Coding Tasks" section to AGENTS.md that says: when spawning Claude Code sessions for coding work, tell the session to use gstack skills. Include these examples — security audit: "Load gstack. Run /cso", code review: "Load gstack. Run /review", QA test a URL: "Load gstack. Run /qa https://...", build a feature end-to-end: "Load gstack. Run /autoplan, implement the plan, then run /ship", plan before building: "Load gstack. Run /office-hours then /autoplan. Save the plan, don't implement." **After setup, just talk to your OpenClaw agent naturally:** @@ -105,7 +105,7 @@ gstack works on 10 AI coding agents, not just Claude. Setup auto-detects which agents you have installed: ```bash -git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/gstack +git clone --single-branch --depth 1 https://github.com/seamfix/gstack.git ~/gstack cd ~/gstack && ./setup ``` diff --git a/TODOS.md b/TODOS.md index 742ecf796e..6b277285ea 100644 --- a/TODOS.md +++ b/TODOS.md @@ -1,5 +1,21 @@ # TODOS +## FORK-LOCAL (seamfix) + +### Enable this fork's own CI workflows (Platform, repo settings) + +**What:** The fork ships 14 workflow files but only CodeQL and Dependabot are active; `free-tests`, `quality-gate`, +`version-gate` and `skill-docs` never run on fork-local PRs (seamfix/gstack#2 review, 2026-09-08). `skill-docs` is the +gate that catches template/generated drift. +**Why:** Fork-local patches are otherwise verified only on the author's machine. +**Effort:** S (repo settings). **Priority:** P2. **Owner:** Platform Engineers (admin). + +### Fork-local changes that every upstream sync must re-apply + +`bin/gstack-team-init`, `bin/gstack-update-check`, `gstack-upgrade/SKILL.md(.tmpl)`, README/CONTRIBUTING/tutorial clone +commands → `seamfix/gstack`. Guarded by the `fork-local:` test in `test/team-mode.test.ts` — if it fails after a sync, +re-apply. Canonical list: seamfix/ai-governance `docs/runbooks/gstack-fork-sync.md`. + ## NEXT PRIORITY ### P1: ZeroEntropy sunset — gbrain's default embedding provider dies Sept 4, 2026 (#2365) diff --git a/bin/gstack-update-check b/bin/gstack-update-check index 3573c7878a..8a2308c36a 100755 --- a/bin/gstack-update-check +++ b/bin/gstack-update-check @@ -50,8 +50,13 @@ CACHE_FILE="$STATE_DIR/last-update-check" MARKER_FILE="$STATE_DIR/just-upgraded-from" SNOOZE_FILE="$STATE_DIR/update-snoozed" VERSION_FILE="$GSTACK_DIR/VERSION" -REMOTE_URL="${GSTACK_REMOTE_URL:-https://raw.githubusercontent.com/garrytan/gstack/main/VERSION}" -REMOTE_REPO="${GSTACK_REMOTE_REPO:-https://github.com/garrytan/gstack.git}" +# Fork-local: compare against the install's OWN origin (seamfix/gstack), never a hardcoded upstream — otherwise every +# fork install is nagged toward upstream forever (fork 1.68.3 vs upstream 1.81.0) while auto-upgrade pulls the fork. +_ORIGIN_URL="$(git -C "$GSTACK_DIR" remote get-url origin 2>/dev/null || true)" +_ORIGIN_REPO="$(printf '%s' "$_ORIGIN_URL" | sed -nE 's#.*github\.com[:/]([^/]+/[^/]+?)(\.git)?/?$#\1#p' | sed 's/\.git$//')" +[ -n "$_ORIGIN_REPO" ] || _ORIGIN_REPO="seamfix/gstack" +REMOTE_URL="${GSTACK_REMOTE_URL:-https://raw.githubusercontent.com/$_ORIGIN_REPO/main/VERSION}" +REMOTE_REPO="${GSTACK_REMOTE_REPO:-https://github.com/$_ORIGIN_REPO.git}" # ─── Force flag (busts cache + snooze for standalone /gstack-upgrade) ── if [ "${1:-}" = "--force" ]; then @@ -237,7 +242,7 @@ if [ -z "${GSTACK_REMOTE_URL:-}" ]; then git ls-remote "$1" refs/heads/main 2>/dev/null' _ "$REMOTE_REPO" || true)" _REMOTE_SHA="$(echo "$_LSR_LINE" | awk '{print $1}')" if echo "$_REMOTE_SHA" | grep -qE '^[0-9a-f]{40}$'; then - _SHA_URL="https://raw.githubusercontent.com/garrytan/gstack/${_REMOTE_SHA}/VERSION" + _SHA_URL="https://raw.githubusercontent.com/$_ORIGIN_REPO/${_REMOTE_SHA}/VERSION" REMOTE="$(_receipted_version_fetch "$_SHA_URL")" fi fi diff --git a/docs/tutorial-document-generate.md b/docs/tutorial-document-generate.md index 7e8e78f452..8f3441d992 100644 --- a/docs/tutorial-document-generate.md +++ b/docs/tutorial-document-generate.md @@ -4,7 +4,7 @@ You'll run `/document-generate` against a project you already have, watch it wri ## What you'll need -- gstack installed (`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`) +- gstack installed (`git clone --single-branch --depth 1 https://github.com/seamfix/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`) - Claude Code running in any project that has at least one piece of public surface (a CLI command, an exported function, a config option, a skill, an API endpoint) - About 90 seconds diff --git a/gstack-upgrade/SKILL.md b/gstack-upgrade/SKILL.md index b5ec03d428..45a97ecf74 100644 --- a/gstack-upgrade/SKILL.md +++ b/gstack-upgrade/SKILL.md @@ -171,7 +171,7 @@ If `$STASH_OUTPUT` contains "Saved working directory", warn the user: "Note: loc ```bash PARENT=$(dirname "$INSTALL_DIR") TMP_DIR=$(mktemp -d) -git clone --depth 1 https://github.com/garrytan/gstack.git "$TMP_DIR/gstack" +git clone --depth 1 https://github.com/seamfix/gstack.git "$TMP_DIR/gstack" mv "$INSTALL_DIR" "$INSTALL_DIR.bak" mv "$TMP_DIR/gstack" "$INSTALL_DIR" cd "$INSTALL_DIR" && ./setup diff --git a/gstack-upgrade/SKILL.md.tmpl b/gstack-upgrade/SKILL.md.tmpl index ca211d919a..0a4def2f14 100644 --- a/gstack-upgrade/SKILL.md.tmpl +++ b/gstack-upgrade/SKILL.md.tmpl @@ -168,7 +168,7 @@ If `$STASH_OUTPUT` contains "Saved working directory", warn the user: "Note: loc ```bash PARENT=$(dirname "$INSTALL_DIR") TMP_DIR=$(mktemp -d) -git clone --depth 1 https://github.com/garrytan/gstack.git "$TMP_DIR/gstack" +git clone --depth 1 https://github.com/seamfix/gstack.git "$TMP_DIR/gstack" mv "$INSTALL_DIR" "$INSTALL_DIR.bak" mv "$TMP_DIR/gstack" "$INSTALL_DIR" cd "$INSTALL_DIR" && {{SETUP_COMMAND}} diff --git a/test/team-mode.test.ts b/test/team-mode.test.ts index ce8c1d6107..739534e03d 100644 --- a/test/team-mode.test.ts +++ b/test/team-mode.test.ts @@ -199,6 +199,20 @@ describe('gstack-team-init', () => { fs.rmSync(tmpDir, { recursive: true, force: true }); }); + test('fork-local: install and upgrade paths point at seamfix/gstack, never upstream', () => { + // Guards the fork-local patch (seamfix/gstack#2). An upstream sync reverts these silently; this test is the tripwire. + run(`${TEAM_INIT} required`, { cwd: tmpDir }); + const claude = fs.readFileSync(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); + expect(claude).toContain('https://github.com/seamfix/gstack.git'); + expect(claude).not.toContain('garrytan/gstack'); + for (const rel of ['bin/gstack-team-init', 'bin/gstack-update-check', 'gstack-upgrade/SKILL.md.tmpl', 'gstack-upgrade/SKILL.md']) { + const src = fs.readFileSync(path.join(ROOT, rel), 'utf-8'); + expect(src, `${rel} still references upstream`).not.toContain('github.com/garrytan/gstack'); + } + const readme = fs.readFileSync(path.join(ROOT, 'README.md'), 'utf-8'); + expect(readme).not.toMatch(/git clone[^\n]*garrytan\/gstack\.git/); + }); + test('errors without a mode argument', () => { const result = run(TEAM_INIT, { cwd: tmpDir }); expect(result.exitCode).not.toBe(0); From 19f34e7ffd29704da38c8c4af42d5b89c62e3951 Mon Sep 17 00:00:00 2001 From: Chibuzor Date: Tue, 8 Sep 2026 16:45:55 +0400 Subject: [PATCH 3/7] update-check: portable origin derivation (the first version used a non-greedy ERE that BSD sed rejects, so the fallback answered, not the derivation); test covers https, https+.git, ssh Co-Authored-By: Claude Fable 5.1 --- bin/gstack-update-check | 8 ++++++-- test/team-mode.test.ts | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/bin/gstack-update-check b/bin/gstack-update-check index 8a2308c36a..ee51559a47 100755 --- a/bin/gstack-update-check +++ b/bin/gstack-update-check @@ -52,8 +52,12 @@ SNOOZE_FILE="$STATE_DIR/update-snoozed" VERSION_FILE="$GSTACK_DIR/VERSION" # Fork-local: compare against the install's OWN origin (seamfix/gstack), never a hardcoded upstream — otherwise every # fork install is nagged toward upstream forever (fork 1.68.3 vs upstream 1.81.0) while auto-upgrade pulls the fork. -_ORIGIN_URL="$(git -C "$GSTACK_DIR" remote get-url origin 2>/dev/null || true)" -_ORIGIN_REPO="$(printf '%s' "$_ORIGIN_URL" | sed -nE 's#.*github\.com[:/]([^/]+/[^/]+?)(\.git)?/?$#\1#p' | sed 's/\.git$//')" +gstack_origin_repo() { # owner/repo of the install's own origin (https or ssh, with or without .git); empty if not GitHub + local u; u="$(git -C "${GSTACK_DIR:-.}" remote get-url origin 2>/dev/null || true)" + u="${u%/}"; u="${u%.git}" + printf '%s' "$u" | sed -nE 's#^.*github\.com[:/]([^/]+/[^/]+)$#\1#p' +} +_ORIGIN_REPO="$(gstack_origin_repo)" [ -n "$_ORIGIN_REPO" ] || _ORIGIN_REPO="seamfix/gstack" REMOTE_URL="${GSTACK_REMOTE_URL:-https://raw.githubusercontent.com/$_ORIGIN_REPO/main/VERSION}" REMOTE_REPO="${GSTACK_REMOTE_REPO:-https://github.com/$_ORIGIN_REPO.git}" diff --git a/test/team-mode.test.ts b/test/team-mode.test.ts index 739534e03d..e05b093b78 100644 --- a/test/team-mode.test.ts +++ b/test/team-mode.test.ts @@ -213,6 +213,21 @@ describe('gstack-team-init', () => { expect(readme).not.toMatch(/git clone[^\n]*garrytan\/gstack\.git/); }); + test('fork-local: update-check derives owner/repo from the install origin (https, https+.git, ssh)', () => { + const fn = execSync(`sed -n '/^gstack_origin_repo()/,/^}/p' bin/gstack-update-check`, { cwd: ROOT, encoding: 'utf-8' }); + expect(fn).toContain('gstack_origin_repo()'); + for (const [url, want] of [ + ['https://github.com/seamfix/gstack.git', 'seamfix/gstack'], + ['https://github.com/seamfix/gstack', 'seamfix/gstack'], + ['git@github.com:seamfix/gstack.git', 'seamfix/gstack'], + ['https://github.com/garrytan/gstack.git', 'garrytan/gstack'], + ]) { + execSync(`git remote remove origin 2>/dev/null; git remote add origin '${url}'`, { cwd: tmpDir, shell: '/bin/bash' }); + const out = run(`bash -c '${fn.replace(/'/g, "'\\''")}; GSTACK_DIR="${tmpDir}" gstack_origin_repo'`); + expect(out.stdout.trim(), url).toBe(want); + } + }); + test('errors without a mode argument', () => { const result = run(TEAM_INIT, { cwd: tmpDir }); expect(result.exitCode).not.toBe(0); From e7e51ae5b8d5cbac595484f9ed1264cbf36dab8b Mon Sep 17 00:00:00 2001 From: Chibuzor Date: Tue, 8 Sep 2026 16:47:37 +0400 Subject: [PATCH 4/7] test: source the origin-derivation function from a file (the quoted-through-two-shells version returned empty) Co-Authored-By: Claude Fable 5.1 --- test/team-mode.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/team-mode.test.ts b/test/team-mode.test.ts index e05b093b78..f089f2db9b 100644 --- a/test/team-mode.test.ts +++ b/test/team-mode.test.ts @@ -214,8 +214,10 @@ describe('gstack-team-init', () => { }); test('fork-local: update-check derives owner/repo from the install origin (https, https+.git, ssh)', () => { - const fn = execSync(`sed -n '/^gstack_origin_repo()/,/^}/p' bin/gstack-update-check`, { cwd: ROOT, encoding: 'utf-8' }); - expect(fn).toContain('gstack_origin_repo()'); + // Extract the function into a file and source it — no quoting through two shells. + const fnFile = path.join(tmpDir, 'origin-fn.sh'); + execSync(`sed -n '/^gstack_origin_repo()/,/^}/p' bin/gstack-update-check > '${fnFile}'`, { cwd: ROOT, shell: '/bin/bash' }); + expect(fs.readFileSync(fnFile, 'utf-8')).toContain('gstack_origin_repo()'); for (const [url, want] of [ ['https://github.com/seamfix/gstack.git', 'seamfix/gstack'], ['https://github.com/seamfix/gstack', 'seamfix/gstack'], @@ -223,7 +225,7 @@ describe('gstack-team-init', () => { ['https://github.com/garrytan/gstack.git', 'garrytan/gstack'], ]) { execSync(`git remote remove origin 2>/dev/null; git remote add origin '${url}'`, { cwd: tmpDir, shell: '/bin/bash' }); - const out = run(`bash -c '${fn.replace(/'/g, "'\\''")}; GSTACK_DIR="${tmpDir}" gstack_origin_repo'`); + const out = run(`bash -c 'source "${fnFile}"; gstack_origin_repo'`, { env: { GSTACK_DIR: tmpDir } }); expect(out.stdout.trim(), url).toBe(want); } }); From 3f2bfa5eefc3796720dbeaa743c809e434ad65e6 Mon Sep 17 00:00:00 2001 From: Chibuzor Date: Tue, 8 Sep 2026 17:02:11 +0400 Subject: [PATCH 5/7] ci: trigger the fork's newly enabled workflows on this PR Co-Authored-By: Claude Fable 5.1 From e4456a816e0744872ee3c8255624c23cf9cd1bf8 Mon Sep 17 00:00:00 2001 From: Chibuzor Date: Tue, 8 Sep 2026 17:26:28 +0400 Subject: [PATCH 6/7] fork-local: workflows run on GitHub-hosted ubuntu-latest (upstream's Ubicloud runner labels queue forever in this org); tripwire covers it Co-Authored-By: Claude Fable 5.1 --- .github/workflows/actionlint.yml | 2 +- .github/workflows/ci-image.yml | 2 +- .github/workflows/dependency-review.yml | 2 +- .github/workflows/evals-periodic.yml | 4 ++-- .github/workflows/evals.yml | 4 ++-- .github/workflows/free-tests.yml | 2 +- .github/workflows/pr-title-sync.yml | 2 +- .github/workflows/quality-gate.yml | 2 +- .github/workflows/skill-docs.yml | 2 +- .github/workflows/version-gate.yml | 2 +- TODOS.md | 2 +- test/team-mode.test.ts | 5 +++++ 12 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 544f694fd6..e2160473c1 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -20,7 +20,7 @@ permissions: jobs: actionlint: - runs-on: ubicloud-standard-2 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: diff --git a/.github/workflows/ci-image.yml b/.github/workflows/ci-image.yml index 4cb1dccc27..3c3c682143 100644 --- a/.github/workflows/ci-image.yml +++ b/.github/workflows/ci-image.yml @@ -15,7 +15,7 @@ on: jobs: build: - runs-on: ubicloud-standard-8 + runs-on: ubuntu-latest permissions: contents: read packages: write diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index b600ada817..6ae31cfafb 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -18,7 +18,7 @@ permissions: jobs: dependency-review: - runs-on: ubicloud-standard-8 + runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: read diff --git a/.github/workflows/evals-periodic.yml b/.github/workflows/evals-periodic.yml index 3017868b9c..fd42e80c76 100644 --- a/.github/workflows/evals-periodic.yml +++ b/.github/workflows/evals-periodic.yml @@ -15,7 +15,7 @@ env: jobs: build-image: - runs-on: ubicloud-standard-8 + runs-on: ubuntu-latest permissions: contents: read packages: write @@ -66,7 +66,7 @@ jobs: ${{ env.IMAGE }}:latest evals: - runs-on: ubicloud-standard-8 + runs-on: ubuntu-latest needs: build-image container: image: ${{ needs.build-image.outputs.image-tag }} diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml index 2a5916dbe0..a64d7ec2f2 100644 --- a/.github/workflows/evals.yml +++ b/.github/workflows/evals.yml @@ -21,7 +21,7 @@ jobs: # needs-chain tolerates it because no eval test selects on a lockfile-only # diff — a maintainer's next push rebuilds the image with real perms. if: github.actor != 'dependabot[bot]' - runs-on: ubicloud-standard-8 + runs-on: ubuntu-latest permissions: contents: read packages: write @@ -348,7 +348,7 @@ jobs: retention-days: 90 report: - runs-on: ubicloud-standard-2 + runs-on: ubuntu-latest needs: evals if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository timeout-minutes: 5 diff --git a/.github/workflows/free-tests.yml b/.github/workflows/free-tests.yml index 7827277726..c1754cb076 100644 --- a/.github/workflows/free-tests.yml +++ b/.github/workflows/free-tests.yml @@ -45,7 +45,7 @@ permissions: jobs: free-tests: - runs-on: ubicloud-standard-8 + runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@v7 diff --git a/.github/workflows/pr-title-sync.yml b/.github/workflows/pr-title-sync.yml index 5a01ae2754..cc76102e93 100644 --- a/.github/workflows/pr-title-sync.yml +++ b/.github/workflows/pr-title-sync.yml @@ -31,7 +31,7 @@ concurrency: jobs: sync: name: Sync PR title to VERSION - runs-on: ubicloud-standard-2 + runs-on: ubuntu-latest permissions: contents: read pull-requests: write diff --git a/.github/workflows/quality-gate.yml b/.github/workflows/quality-gate.yml index 5760d1a6b0..6088e1c182 100644 --- a/.github/workflows/quality-gate.yml +++ b/.github/workflows/quality-gate.yml @@ -28,7 +28,7 @@ concurrency: jobs: quality: - runs-on: ubicloud-standard-8 + runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4 diff --git a/.github/workflows/skill-docs.yml b/.github/workflows/skill-docs.yml index 47ba5f36bf..aed84e32d4 100644 --- a/.github/workflows/skill-docs.yml +++ b/.github/workflows/skill-docs.yml @@ -15,7 +15,7 @@ concurrency: jobs: check-freshness: - runs-on: ubicloud-standard-2 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/version-gate.yml b/.github/workflows/version-gate.yml index 00a2e25ecd..f07108dd9e 100644 --- a/.github/workflows/version-gate.yml +++ b/.github/workflows/version-gate.yml @@ -14,7 +14,7 @@ concurrency: jobs: check: name: Check VERSION is not stale vs queue - runs-on: ubicloud-standard-2 + runs-on: ubuntu-latest permissions: contents: read pull-requests: read diff --git a/TODOS.md b/TODOS.md index 6b277285ea..565f1b50f7 100644 --- a/TODOS.md +++ b/TODOS.md @@ -13,7 +13,7 @@ gate that catches template/generated drift. ### Fork-local changes that every upstream sync must re-apply `bin/gstack-team-init`, `bin/gstack-update-check`, `gstack-upgrade/SKILL.md(.tmpl)`, README/CONTRIBUTING/tutorial clone -commands → `seamfix/gstack`. Guarded by the `fork-local:` test in `test/team-mode.test.ts` — if it fails after a sync, +commands → `seamfix/gstack`; `.github/workflows/*.yml` `runs-on: ubuntu-latest` (upstream uses Ubicloud runners this org doesn't have — jobs queue forever). Guarded by the `fork-local:` test in `test/team-mode.test.ts` — if it fails after a sync, re-apply. Canonical list: seamfix/ai-governance `docs/runbooks/gstack-fork-sync.md`. ## NEXT PRIORITY diff --git a/test/team-mode.test.ts b/test/team-mode.test.ts index f089f2db9b..d47f0651fb 100644 --- a/test/team-mode.test.ts +++ b/test/team-mode.test.ts @@ -211,6 +211,11 @@ describe('gstack-team-init', () => { } const readme = fs.readFileSync(path.join(ROOT, 'README.md'), 'utf-8'); expect(readme).not.toMatch(/git clone[^\n]*garrytan\/gstack\.git/); + // Workflows must run on GitHub-hosted runners: the seamfix org has no Ubicloud runners, so upstream's labels queue forever. + for (const wf of fs.readdirSync(path.join(ROOT, '.github', 'workflows')).filter(f => f.endsWith('.yml'))) { + const y = fs.readFileSync(path.join(ROOT, '.github', 'workflows', wf), 'utf-8'); + expect(y, `${wf} requests a ubicloud runner`).not.toMatch(/runs-on:\s*ubicloud/); + } }); test('fork-local: update-check derives owner/repo from the install origin (https, https+.git, ssh)', () => { From 2a3c7dcd8bfec6fcb9a4bc65d012391fece1c488 Mon Sep 17 00:00:00 2001 From: Chibuzor Date: Tue, 8 Sep 2026 17:27:43 +0400 Subject: [PATCH 7/7] fork-local: last Ubicloud references (make-pdf-gate step conditions, evals default runner) Co-Authored-By: Claude Fable 5.1 --- .github/workflows/evals.yml | 6 +++--- .github/workflows/make-pdf-gate.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml index a64d7ec2f2..4c6d23a6b1 100644 --- a/.github/workflows/evals.yml +++ b/.github/workflows/evals.yml @@ -86,7 +86,7 @@ jobs: # Same-repo PRs, pushes, and workflow_dispatch keep full coverage. Fork work # gets real coverage via a trusted base-repo branch. evals: - runs-on: ${{ matrix.suite.runner || 'ubicloud-standard-8' }} + runs-on: ${{ matrix.suite.runner || 'ubuntu-latest' }} needs: build-image if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository container: @@ -104,7 +104,7 @@ jobs: file: test/skill-llm-eval.test.ts - name: e2e-browse file: test/skill-e2e-bws.test.ts - runner: ubicloud-standard-8 + runner: ubuntu-latest - name: e2e-plan file: test/skill-e2e-plan.test.ts - name: e2e-deploy @@ -418,7 +418,7 @@ jobs: $(echo -e "$SUITE_LINES") --- - *ubicloud-standard-8 runners (Docker: pre-baked toolchain + deps) | wall clock ≈ slowest suite*" + *ubuntu-latest runners (Docker: pre-baked toolchain + deps) | wall clock ≈ slowest suite*" if [ "$FAILED" -gt 0 ]; then FAILURES="" diff --git a/.github/workflows/make-pdf-gate.yml b/.github/workflows/make-pdf-gate.yml index fa98082764..27a493de9a 100644 --- a/.github/workflows/make-pdf-gate.yml +++ b/.github/workflows/make-pdf-gate.yml @@ -54,13 +54,13 @@ jobs: run: brew install poppler - name: Install poppler-utils (Ubuntu) - if: matrix.os == 'ubicloud-standard-8' + if: matrix.os == 'ubuntu-latest' run: sudo apt-get update && sudo apt-get install -y poppler-utils # Install a color-emoji font BEFORE Chromium launches so the emoji render # gate has a fallback font. macOS ships Apple Color Emoji already. - name: Install color-emoji font (Ubuntu) - if: matrix.os == 'ubicloud-standard-8' + if: matrix.os == 'ubuntu-latest' run: | sudo apt-get install -y fonts-noto-color-emoji fc-cache -f || true