From 8509b075dc9f4114f65b80c68308e027d5ce51bd Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sat, 31 Oct 2026 19:41:04 +0200 Subject: [PATCH 1/6] ci: bump omp to v18.14.1, bun to 1.4.4, and checkout to v7 The smoke pinned omp v18.12.6, six releases behind. The v18.14.1 digest matches the release asset and the sha256 of the downloaded file. actions/checkout@v4 runs on Node.js 20, and every job warned about it. --- .github/workflows/gate.yml | 20 ++++++++++---------- .github/workflows/upstream-sync.yml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index 415ade6..c6bafe1 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -14,10 +14,10 @@ jobs: gate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: - bun-version: 1.4.2 + bun-version: 1.4.4 - name: Shell syntax # One bash -n per file, in a loop. `bash -n a.sh b.sh` parses a.sh and passes b.sh through as # a positional parameter, so the single-command form checks exactly one file and reports @@ -51,18 +51,18 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: - bun-version: 1.4.2 + bun-version: 1.4.4 - name: Install pinned omp - # can1357/oh-my-pi release v18.12.6, asset omp-linux-x64, sha256 - # 85d9daf3a05fc76e8cd425ae1904eb576af8d21cc85fcf4690742626e07ccfa4. That is the digest - # `gh api repos/can1357/oh-my-pi/releases/latest` reports for the asset and the sha256 of the - # downloaded file, checked 2026-10-22. A bump changes the tag and the digest together. + # can1357/oh-my-pi release v18.14.1, asset omp-linux-x64, sha256 + # 02d76a0cfeee473ffa387f50cf0378b0de1c44684c83e67ca27ba4d717cfb0d0. That is the digest + # `gh release view v18.14.1 -R can1357/oh-my-pi --json assets` reports for the asset and the + # sha256 of the downloaded file, checked 2026-10-31. A bump changes the tag and the digest together. env: - OMP_TAG: v18.12.6 - OMP_SHA256: 85d9daf3a05fc76e8cd425ae1904eb576af8d21cc85fcf4690742626e07ccfa4 + OMP_TAG: v18.14.1 + OMP_SHA256: 02d76a0cfeee473ffa387f50cf0378b0de1c44684c83e67ca27ba4d717cfb0d0 run: | dir="$RUNNER_TEMP/omp" mkdir -p "$dir" diff --git a/.github/workflows/upstream-sync.yml b/.github/workflows/upstream-sync.yml index 4dc7646..282a5ef 100644 --- a/.github/workflows/upstream-sync.yml +++ b/.github/workflows/upstream-sync.yml @@ -23,7 +23,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 From 974e48cfae6e9b2c99f373ccc243fd45adc00723 Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sat, 31 Oct 2026 19:41:12 +0200 Subject: [PATCH 2/6] ci(gate): check every shell script, extension test, and the statechart suite The syntax step read only omp-port/*.sh, and the extension step named two test files, so a new script or extension test was never checked. The statechart suite had no CI step at all. It runs on Node 22 through the package's own test script and the fast-check pin in its package.json. --- .github/workflows/gate.yml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index c6bafe1..03122a5 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -21,9 +21,9 @@ jobs: - name: Shell syntax # One bash -n per file, in a loop. `bash -n a.sh b.sh` parses a.sh and passes b.sh through as # a positional parameter, so the single-command form checks exactly one file and reports - # success for the rest, which is worse than no step: it looks like four scripts are covered. + # success for the rest, which is worse than no step: it looks like every script is covered. run: | - for f in omp-port/*.sh; do + for f in $(git ls-files '*.sh'); do echo "::group::$f" bash -n "$f" echo "::endgroup::" @@ -43,7 +43,7 @@ jobs: run: bun run typecheck - name: Extension tests working-directory: plugins/pstack - run: bun test ./extensions/potetomode/index.test.js ./extensions/pstackpolicy/index.test.js + run: bun test ./extensions smoke: # A real omp loads plugins/pstack: every skill directory must register as a command, and the @@ -75,3 +75,20 @@ jobs: run: OMP_SMOKE_REQUIRED=1 bun omp-port/smoke-plugin.mjs - name: Worktree audit against live omp run: OMP_AUDIT_LIVE_REQUIRED=1 bash omp-port/test-worktree-audit.sh + + statechart: + runs-on: ubuntu-latest + timeout-minutes: 10 + defaults: + run: + working-directory: plugins/pstack/skills/statechart/scripts + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 22 + - name: Install test dependencies + run: npm install --no-package-lock + - name: Statechart tests + # The cases that need TLC skip, because the runner sets no TLA2TOOLS. + run: npm test From a5ada06e18d51cb3fa993fd222cb363616b02f5d Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sat, 31 Oct 2026 19:41:24 +0200 Subject: [PATCH 3/6] ci(upstream-sync): merge a sync only after gate.yml passes on it The sync job opened the PR and squash-merged it in one step, after check-port.sh alone. PRs #19 and #32 landed 3 to 4 seconds after they opened, and no gate.yml run checked either one. gate.yml now takes a workflow_call ref. upstream-sync splits into sync, gate, and merge jobs. The gate job calls gate.yml on the sync commit, and the merge job needs both earlier jobs to pass and the PR head to still match that commit. PORTING.md drops the claim that a token-opened PR starts no pull_request run. --- .github/workflows/gate.yml | 12 ++++++++++++ .github/workflows/upstream-sync.yml | 29 +++++++++++++++++++++++++++-- PORTING.md | 10 +++++++--- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index 03122a5..a01eb31 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -9,12 +9,20 @@ on: types: [opened, synchronize, reopened, edited] push: branches: [main] + workflow_call: + inputs: + ref: + description: Commit to gate. upstream-sync passes its sync commit and merges only after this run passes. + type: string + required: true jobs: gate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref }} - uses: oven-sh/setup-bun@v2 with: bun-version: 1.4.4 @@ -52,6 +60,8 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref }} - uses: oven-sh/setup-bun@v2 with: bun-version: 1.4.4 @@ -84,6 +94,8 @@ jobs: working-directory: plugins/pstack/skills/statechart/scripts steps: - uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref }} - uses: actions/setup-node@v7 with: node-version: 22 diff --git a/.github/workflows/upstream-sync.yml b/.github/workflows/upstream-sync.yml index 282a5ef..910d858 100644 --- a/.github/workflows/upstream-sync.yml +++ b/.github/workflows/upstream-sync.yml @@ -20,6 +20,9 @@ permissions: jobs: sync: runs-on: ubuntu-latest + outputs: + pr: ${{ steps.build.outputs.pr }} + sha: ${{ steps.build.outputs.sha }} env: GH_TOKEN: ${{ github.token }} steps: @@ -70,7 +73,8 @@ jobs: TARGET: ${{ steps.plan.outputs.target }} run: bash omp-port/check-port.sh "" "$TARGET" - - if: steps.plan.outputs.go == 'yes' + - id: build + if: steps.plan.outputs.go == 'yes' env: TARGET: ${{ steps.plan.outputs.target }} SHORT: ${{ steps.plan.outputs.short }} @@ -106,8 +110,29 @@ jobs: pr=$(gh pr create --base main --head "sync/upstream-$SHORT" \ --title "sync: upstream pstack $SHORT ($version)" --body-file "$RUNNER_TEMP/body.md" | tail -n1) if [ "$code" -eq 0 ] && [ "$gate" -eq 0 ]; then - gh pr merge "$pr" --squash --delete-branch + printf 'pr=%s\nsha=%s\n' "$pr" "$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" exit 0 fi echo "left $pr open for a human: sync exit $code, gate exit $gate" exit 1 + + # A merge made with github.token starts no push run on main, so this job is the only full gate a + # sync gets. A red gate skips the merge and leaves the PR open for a human. + gate: + needs: sync + if: needs.sync.outputs.sha != '' + permissions: + contents: read + uses: ./.github/workflows/gate.yml + with: + ref: ${{ needs.sync.outputs.sha }} + + merge: + needs: [sync, gate] + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ needs.sync.outputs.pr }} + SHA: ${{ needs.sync.outputs.sha }} + steps: + - run: gh pr merge "$PR" --repo "$GITHUB_REPOSITORY" --squash --delete-branch --match-head-commit "$SHA" diff --git a/PORTING.md b/PORTING.md index dfe06e7..6f8d898 100644 --- a/PORTING.md +++ b/PORTING.md @@ -103,9 +103,13 @@ question locally: bash omp-port/check-port.sh "" "$(gh api 'repos/cursor/plugins/commits?path=pstack&sha=main&per_page=1' --jq '.[0].sha')" ``` -The gate runs as its own workflow, `gate.yml`, on every pull request and on every push to `main`. A -PR opened with `github.token` does not trigger `pull_request` workflows, so the sync PR carries the -gate output in its body and the `push` to `main` run after the merge is the backstop. +The gate runs as its own workflow, `gate.yml`, on every pull request and on every push to `main`. +Neither trigger gated the two syncs the bot merged, #19 and #32. Each PR got a `pull_request` run +that ended with no jobs a second after the merge, and the merge, made with `github.token`, started +no `push` run. So `upstream-sync` calls `gate.yml` itself, as a reusable workflow on the sync +commit, and its `merge` job runs only after that call passes. A sync that fails the build, +`check-port.sh`, or `gate.yml` stays open for a human, and later runs stop at the open-PR guard +until someone resolves it. Never substitute the `backnotprop/pstack` mirror for upstream. From 983bd1b282b05922bdbdfc03bec36ff199f2bdb5 Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sat, 31 Oct 2026 19:41:31 +0200 Subject: [PATCH 4/6] docs(porting): re-confirm two omp claims on 18.14.1 The builtin tool probe on the v18.14.1 release asset lists wait and no hub. `omp read omp://approval-mode.md` on that binary still states the three approval facts. The 18.12.2 and 18.1.13 lines keep their versions because their probes did not run again. --- PORTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PORTING.md b/PORTING.md index 6f8d898..5853f06 100644 --- a/PORTING.md +++ b/PORTING.md @@ -324,7 +324,7 @@ The injected reminder is a pointer, not the playbook. It tells the agent to read tool blocks until the next settled job or peer message. These are the sanctioned primitives when workers must coordinate instead of running independent. There is no `hub` tool. Probe it rather than trusting this line: `grep -a -A40 'src/tools/builtin-names.ts' "$(readlink -f "$(command -v omp)")"` - prints the binary's builtin tool list, which on 18.12.6 carries `wait` and no `hub`; the binary's + prints the binary's builtin tool list, which on 18.14.1 carries `wait` and no `hub`; the binary's only other `hub` is the `/hub` slash command that opens Agent Hub. An earlier tree read a `hub` tool with list/send/inbox/wait ops off a stale pi-coding-agent 17.x package, and the gate now fails any shipped sentence that names one. The orchestrator uses its own TSV store and @@ -398,7 +398,7 @@ thinking id for judgment work. ### What omp can actually stop The playbooks say "stop where the human's call begins", and until recently that was advice with -nothing behind it. Three facts from `omp://approval-mode.md`, on omp 18.4.10, decide what an +nothing behind it. Three facts from `omp://approval-mode.md`, on omp 18.14.1, decide what an instruction is worth here: - `yolo` auto-approves the `read`, `write`, **and `exec`** tiers. A `gh pr merge` inside a bash From b2fbfd3d92f4507a6bff3e0952dcb74f08257fe2 Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sat, 31 Oct 2026 20:55:22 +0200 Subject: [PATCH 5/6] docs(ci): describe the real-turn smoke legs --- .github/workflows/gate.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index a01eb31..781d812 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -54,8 +54,9 @@ jobs: run: bun test ./extensions smoke: - # A real omp loads plugins/pstack: every skill directory must register as a command, and the - # pin extension must record and reload its state. omp-port/smoke-plugin.mjs names each check. + # A real omp loads plugins/pstack through -e, `omp plugin link`, and a local marketplace install, + # and runs scripted turns: every skill registers, the pinned prompt names poteto-mode, and + # pstackpolicy refuses a merge before gh runs. omp-port/smoke-plugin.mjs names each check. runs-on: ubuntu-latest timeout-minutes: 10 steps: From 7eb866c01e3515e91db3e25d4cd965a959f63756 Mon Sep 17 00:00:00 2001 From: Negrescu Alexandru Date: Sat, 31 Oct 2026 21:06:20 +0200 Subject: [PATCH 6/6] ci: drop setup-pstack script tests, removed in #39 --- .github/workflows/gate.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index 781d812..fded20e 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -44,8 +44,6 @@ jobs: - name: Script tests working-directory: plugins/pstack/skills/poteto-mode/scripts run: bun run test - - name: Setup script tests - run: bun test plugins/pstack/skills/setup-pstack/scripts - name: Script typecheck working-directory: plugins/pstack/skills/poteto-mode/scripts run: bun run typecheck