Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 45 additions & 15 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,29 @@ 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@v4
- uses: actions/checkout@v7
with:
ref: ${{ inputs.ref }}
- 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
# 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::"
Expand All @@ -41,26 +49,29 @@ 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
# 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:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
ref: ${{ inputs.ref }}
- 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"
Expand All @@ -73,3 +84,22 @@ 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
with:
ref: ${{ inputs.ref }}
- 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
31 changes: 28 additions & 3 deletions .github/workflows/upstream-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ permissions:
jobs:
sync:
runs-on: ubuntu-latest
outputs:
pr: ${{ steps.build.outputs.pr }}
sha: ${{ steps.build.outputs.sha }}
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0

Expand Down Expand Up @@ -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 }}
Expand Down Expand Up @@ -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"
14 changes: 9 additions & 5 deletions PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,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.

Expand Down Expand Up @@ -367,7 +371,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. Cross-session messaging does not exist locally and
Expand Down Expand Up @@ -439,7 +443,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
Expand Down
Loading