From 62710e4af12e1248ae4c710d6fd104c2ae95c260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Tue, 28 Jul 2026 14:37:14 +0200 Subject: [PATCH 1/3] fix(workflow): append tmux windows instead of auto-indexing tmux new-window bare auto-index allocation can fail with 'index N in use' on sessions with stale index state, killing every role dispatch. Windows are tracked by name, so -a (append) is always safe. Includes the matching learning. --- .kilo_workflow/dispatch-role.sh | 6 +++++- .../learnings/tmux-new-window-index-in-use.md | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .kilo_workflow/learnings/tmux-new-window-index-in-use.md diff --git a/.kilo_workflow/dispatch-role.sh b/.kilo_workflow/dispatch-role.sh index 939c925439..671d0e4b27 100755 --- a/.kilo_workflow/dispatch-role.sh +++ b/.kilo_workflow/dispatch-role.sh @@ -38,6 +38,10 @@ CMD+=" > $(printf '%q' "$LOG") 2>&1; echo EXITCODE=\$? >> $(printf '%q' "$LOG")" if [ "$ROLE" = "e2e-verifier" ]; then tmux new-session -d -s "$NAME" "$CMD" else - tmux new-window -d -t "$(tmux display-message -p '#S')" -n "$NAME" "$CMD" + # -a (append): bare auto-index allocation can fail with "index N in use" on + # sessions whose internal index counter is stale (see + # learnings/tmux-new-window-index-in-use.md). Windows are tracked by name, + # never by index, so append placement is always safe. + tmux new-window -da -t "$(tmux display-message -p '#S')" -n "$NAME" "$CMD" fi echo "$LOG" diff --git a/.kilo_workflow/learnings/tmux-new-window-index-in-use.md b/.kilo_workflow/learnings/tmux-new-window-index-in-use.md new file mode 100644 index 0000000000..a104df3f91 --- /dev/null +++ b/.kilo_workflow/learnings/tmux-new-window-index-in-use.md @@ -0,0 +1,16 @@ +# tmux new-window fails with "index N in use" on a session with stale index state + +Symptom: `tmux new-window -d -t -n ` fails with +`create window failed: index 1 in use` even though the session plainly has windows 0 and 1 and +index 2 is free. Every role-agent dispatch through `dispatch-role.sh` dies at window creation. + +Cause: unknown — the session's automatic index allocation is wedged (observed 2026-07-28 on a +starter-created session minutes old; fresh sessions created the same way allocate fine, and it is +not the active-window index, session options, or hooks). Not worth deeper diagnosis: window index +placement is meaningless to the workflow, which tracks windows by name. + +Fix: append instead of auto-allocating — `tmux new-window -da -t -n `. +`-a` inserts after the target window and never contends for a specific index. Fixed in +`dispatch-role.sh`; if a hand-rolled `tmux new-window` ever fails this way, add `-a` there too. +Diagnostic for a recurrence: `tmux new-window -d -t :` succeeds, +confirming allocation (not window creation itself) is the broken part. From daa97f5bee3bdff6a6adc495b319943c82e64599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Tue, 28 Jul 2026 14:44:58 +0200 Subject: [PATCH 2/3] docs(workflow): learn that oxfmt formats YAML workflow files --- .../oxfmt-formats-yaml-workflow-files.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .kilo_workflow/learnings/oxfmt-formats-yaml-workflow-files.md diff --git a/.kilo_workflow/learnings/oxfmt-formats-yaml-workflow-files.md b/.kilo_workflow/learnings/oxfmt-formats-yaml-workflow-files.md new file mode 100644 index 0000000000..7124e4d12e --- /dev/null +++ b/.kilo_workflow/learnings/oxfmt-formats-yaml-workflow-files.md @@ -0,0 +1,16 @@ +# oxfmt formats YAML — workflow files must pass `pnpm format:check` too + +Symptom: `pnpm format:check` fails on a newly written GitHub Actions workflow YAML even when the +content was copied verbatim from a reviewed plan whose evidence claimed "oxfmt has no YAML +coverage". + +Cause: oxfmt (repo root formatter, 0.40.0) does cover `*.yml`/`*.yaml` — observed 2026-07-28 on +`.github/workflows/extension-store-release.yml`, where it collapsed comment-alignment whitespace +(`contents: write # comment` → `contents: write # comment`). A plan written against the +"no YAML coverage" assumption produces files that fail the check, and `ci.yml` enforces +format-check on every PR. + +Fix: after writing or editing workflow YAML, run `pnpm format` and accept oxfmt's rewrite before +declaring the slice done. Block-scalar (`run: |`) bodies are preserved — including heredoc +indentation — but diff the formatter's output against the intended content before committing so a +future formatter version cannot silently alter load-bearing whitespace. From 84347960ac7b30976b3fa34136a3bb2ef0c24c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Tue, 28 Jul 2026 14:52:07 +0200 Subject: [PATCH 3/3] feat(ci): add extension store release workflow workflow_dispatch build of store-submittable extension zips (Chrome MV3, Firefox MV3 + AMO sources) uploaded as a version-named artifact, gated on pnpm --filter kilo-extension verify, with a pending-bump guard that fails a re-press fast while any extension-version-bump-* branch exists. After a successful main build, a second job (the only one with write permissions) patch-bumps apps/extension/package.json and opens the bump PR so the next dispatch produces a higher version. --- .github/workflows/extension-store-release.yml | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 .github/workflows/extension-store-release.yml diff --git a/.github/workflows/extension-store-release.yml b/.github/workflows/extension-store-release.yml new file mode 100644 index 0000000000..a47cdf3259 --- /dev/null +++ b/.github/workflows/extension-store-release.yml @@ -0,0 +1,156 @@ +name: Extension Store Release + +# Push-button build of store-submittable extension zips for marketing: Chrome MV3 +# (Chrome Web Store), Firefox MV3 + sources (Firefox Add-ons). Uploads the zips as +# a run artifact named after the built version, then — on main only — patch-bumps +# apps/extension/package.json and opens the bump PR so the next run produces a +# higher version (stores reject re-used version numbers). + +on: + workflow_dispatch: + +concurrency: + group: extension-store-release + cancel-in-progress: false + +permissions: + contents: read + +jobs: + build-artifacts: + runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} + timeout-minutes: 30 + env: + # Works the day a human adds the secret; wxt.config.ts only warns and + # disables analytics when it is empty. Do not fail when unset. + VITE_POSTHOG_API_KEY: ${{ secrets.VITE_POSTHOG_API_KEY }} + steps: + - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 + with: + lfs: true + + - name: Setup pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + + - name: Setup Node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + cache: 'pnpm' + + - name: Read version, guard against a pending bump + id: version + run: | + CURRENT=$(node -p "require('./apps/extension/package.json').version") + echo "Building version $CURRENT" + PENDING=$(git ls-remote --heads origin 'extension-version-bump-*' | awk '{print $2}' | paste -sd' ' -) + if [ -n "$PENDING" ]; then + echo "::error::Pending version-bump branch(es): $PENDING — merge the open bump PR, or close it and delete the branch (git push origin --delete ), then press again. Re-using a version produces artifacts the stores reject." + exit 1 + fi + echo "version=$CURRENT" >> "$GITHUB_OUTPUT" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Verify (typecheck, lint, format, unit tests) + run: pnpm --filter kilo-extension verify + + - name: Zip (Chrome MV3) + run: pnpm --filter kilo-extension zip + + - name: Zip (Firefox MV3 + AMO sources) + run: pnpm --filter kilo-extension zip:firefox + + - name: Confirm expected zips exist + run: | + V="${{ steps.version.outputs.version }}" + ls -l "apps/extension/.output/kilo-extension-$V-chrome.zip" \ + "apps/extension/.output/kilo-extension-$V-firefox.zip" \ + "apps/extension/.output/kilo-extension-$V-sources.zip" + + - name: Upload store artifacts + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: extension-store-${{ steps.version.outputs.version }} + path: | + apps/extension/.output/kilo-extension-${{ steps.version.outputs.version }}-chrome.zip + apps/extension/.output/kilo-extension-${{ steps.version.outputs.version }}-firefox.zip + apps/extension/.output/kilo-extension-${{ steps.version.outputs.version }}-sources.zip + if-no-files-found: error + overwrite: true + # Store handoff can take weeks; 30 days beats the repo's 7-day CI-debug + # norm while keeping the ~74 MB sources zips from piling up forever. + retention-days: 30 + + bump-version: + needs: build-artifacts + # Only a main dispatch bumps: a branch dispatch would open a PR carrying + # unrelated commits. + if: github.ref == 'refs/heads/main' + runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} + timeout-minutes: 10 + permissions: + contents: write # push the bump branch + pull-requests: write # open the PR, request the reviewer + issues: write # PR assignees are an Issues API operation; pull-requests: write alone does not cover --assignee + steps: + - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 + + - name: Setup Node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + + - name: Bump patch version + id: bump + working-directory: apps/extension + run: | + OLD=$(node -p "require('./package.json').version") + npm version patch --no-git-tag-version + NEW=$(node -p "require('./package.json').version") + echo "Bumped $OLD -> $NEW" + echo "old=$OLD" >> "$GITHUB_OUTPUT" + echo "new=$NEW" >> "$GITHUB_OUTPUT" + + - name: Push bump branch + run: | + NEW="${{ steps.bump.outputs.new }}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "extension-version-bump-$NEW" + git add apps/extension/package.json + git commit -m "chore(extension): bump version to $NEW" + git push -u origin "extension-version-bump-$NEW" + + - name: Open bump PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + NEW="${{ steps.bump.outputs.new }}" + OLD="${{ steps.bump.outputs.old }}" + RUN_URL="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" + cat > /tmp/bump-pr-body.md < $NEW. + + ## Why + + The Extension Store Release workflow built store-submittable artifacts for + version $OLD (run: $RUN_URL). Stores reject re-used version numbers, so the + version is patch-bumped after every successful artifact build to keep the + next build submittable. + + ## What to do + + 1. Required checks show as pending: PRs opened with the default GITHUB_TOKEN + do not trigger workflow runs. Close and reopen this PR (or push an empty + commit) to start CI. + 2. Review and merge. Only the version line changed; no product code. + EOF + gh pr create --base main \ + --title "chore(extension): bump version to $NEW" \ + --body-file /tmp/bump-pr-body.md \ + --assignee iscekic \ + --reviewer iscekic