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
2 changes: 1 addition & 1 deletion .claude/skills/verify-open-pstack/features/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"nonRuntime": [
"README.md", "REVIEW.md", "CHANGES.md", "LICENSE", "LICENSE.md", "UPSTREAM.md",
"NOTICE.md", "README-UPSTREAM.md", "LICENSE-cursor-team-kit", "LICENSE-superpowers",
".gitignore", ".mergify.yml", "docs/**", "assets/**",
".gitignore", ".mergify.yml", ".github/workflows/unfret-threads.yml", "docs/**", "assets/**",
Comment thread
github-actions[bot] marked this conversation as resolved.
"plugins/pstack/skills/poteto-mode/scripts/bootstrap.test.ts",
"plugins/pstack/skills/poteto-mode/scripts/check-plan.test.ts",
"plugins/pstack/skills/poteto-mode/scripts/resolve-codex-transcript.test.ts", "scripts/release.py", "scripts/release_test.py"
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/unfret-threads.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Unfret threads

on:
pull_request_review:
types: [submitted]
Comment thread
github-actions[bot] marked this conversation as resolved.

permissions:
# GitHub refuses resolveReviewThread to the Actions token without contents: write.
contents: write
pull-requests: write

jobs:
resolve:
if: github.event.review.user.login == 'unfret-eal[bot]'
runs-on: ubuntu-latest
steps:
- name: Resolve the threads Unfret started
shell: bash
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
gh api graphql --paginate \
-f owner="${REPOSITORY%/*}" -f name="${REPOSITORY#*/}" -F number="$PR" \
-f query='query($owner: String!, $name: String!, $number: Int!, $endCursor: String) {
repository(owner: $owner, name: $name) {
pullRequest(number: $number) {
reviewThreads(first: 100, after: $endCursor) {
pageInfo { hasNextPage endCursor }
nodes { id isResolved comments(first: 1) { nodes { author { login } } } }
}
}
}
}' \
--jq '.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved | not)
| select(.comments.nodes[0].author.login == "unfret-eal")
| .id' |
while read -r thread; do
gh api graphql -f thread="$thread" \
-f query='mutation($thread: ID!) { resolveReviewThread(input: {threadId: $thread}) { thread { id } } }' \
> /dev/null
done
11 changes: 8 additions & 3 deletions .mergify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ queue_rules:
queue_conditions:
- -draft
- check-success=verify
- check-success=Unfret
- check-success=live-gate
- "#review-threads-unresolved = 0"
merge_conditions: []
Expand All @@ -21,15 +20,21 @@ merge_protections_settings:

merge_protections:
- name: Queue ready pull requests
description: Automatically queue non-draft pull requests targeting main after verify, Unfret, and live-gate pass on the head and every review thread is resolved.
description: Automatically queue non-draft pull requests targeting main after verify and live-gate pass on the head and every review thread is resolved.
if:
- base = main
success_conditions:
- -draft
- check-success=verify
- check-success=Unfret
- check-success=live-gate
- "#review-threads-unresolved = 0"
- name: Unfret on risky paths
description: Pull requests that touch .github/ or the Mergify config also wait for Unfret.
if:
- base = main
- files ~= ^(\.github/|\.mergify(\.ya?ml\Z|/))
success_conditions:
- check-success = @unfret-eal/Unfret

commands_restrictions:
requeue:
Expand Down
8 changes: 7 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ Code this repository owns explains itself. `UPSTREAM.md`, `CHANGES.md` and `NOTI

Before opening a pull request, run the Bun tests, strict typecheck, static invariants, and plugin validation.

Nothing merges, tags, releases, or rolls out until the exact candidate is installed and the changed behavior passes a live test from the real user surface in every affected harness. Unit tests, validators, source inspection, and self-reports do not satisfy this gate. The record is the `live-gate` commit status on that exact head, which links an evidence comment naming the installed version, surface, action, and observed result. Mergify does not queue a pull request without it. A pull request may be ready for review before its live test, but every new head, including a queue-created head, needs its own.
Nothing merges, tags, releases, or rolls out until the exact candidate is installed and the changed behavior passes a live test from the real user surface in every affected harness. Unit tests, validators, source inspection, and self-reports do not satisfy this gate. The live test covers the changed behavior, not every changed file or branch. `verify-open-pstack`'s rule picks the features and never defaults to a full sweep. A representative selection is the default, not a coverage gap to escalate. One stand-in covers a sentence repeated verbatim across many skills: the cheapest skill that carries it, never one that runs several models at high effort, such as `interrogate`, `swarm` or `arena`. The record is the `live-gate` commit status on that exact head, which links an evidence comment naming the installed version, surface, action, and observed result. Mergify does not queue a pull request without it. A pull request may be ready for review before its live test, but every new head, including a queue-created head, needs its own.

For Open Pstack PRs, use the shared repository-local `.claude/skills/verify-open-pstack/` skill (Codex: `.agents/skills/verify-open-pstack`). Verifier-changing PRs run from a checkout of their reviewed candidate head, with the publisher revision equal to the candidate SHA; all other PRs run from trusted `main`. Record the publisher revision separately from the candidate SHA. Reuse the operator's existing logins without credential source flags, extra logins, or credential reading/copying. Claude uses normal operator config with session-only `--plugin-dir <exact-head checkout>/plugins/pstack --settings '{"enabledPlugins":{"pstack@open-pstack":false}}'`. Codex installs the exact-head candidate in a run-owned `CODEX_HOME`, with `auth.json` symlinked to the operator's `${CODEX_HOME:-$HOME/.codex}/auth.json`; absent file-based auth fails closed. Removing the run home removes only the symlink, never its target. Setup alone snapshots real `${CLAUDE_CONFIG_DIR:-$HOME/.claude}/pstack-models.md` and `CLAUDE.md`, restores their exact bytes or original absence afterwards, and verifies restoration before any publication; restoration failure publishes nothing. Preserve private mode-0700 raw evidence through merge. Publication is one structured evidence comment plus exact-SHA `live-gate`; no credential parsing, token registration, or exact-secret comment checks. No PR-body, ready/draft, reversal, or compensation writes, publisher-code attestation, or OS filesystem-denial claims are permitted. Builders do not publish the gate, queue, or merge.

Merge policy: land on green

Mergify merges a pull request once `verify` and `live-gate` pass on its head and no review thread is unresolved. Unfret reviews every head in parallel and usually publishes its verdict after the merge. `.github/workflows/unfret-threads.yml` resolves each thread Unfret starts, so only other reviewers' threads hold a merge. A pull request that touches `.github/**` or the Mergify config also waits for `Unfret`; the risky-path rule in `.mergify.yml` lists those paths, and the verifier is not one of them. A pull request that touches `.github/**` also needs an operator to queue it. A material Unfret finding on a merged head gets a fix-forward pull request: a new, small pull request under the same flow, whose issue carries `Introduced by: <PR URL>`. Mergify reads no `hold` label here: to keep a pull request from landing, keep it a draft.

Publish a release only when every pull request merged since the previous release has an Unfret verdict on its merged head and each material finding among them has a merged fix-forward pull request. Then run `python3 scripts/release.py publish` from a clean checkout of `main`.

Do not add an implicit runtime timeout or a weaker-model fallback.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CHANGES — applied substitutions

Mergify auto-queue is live: ready PRs to main require passing verify, Unfret and exact-head live-gate, and no unresolved review thread; workflow changes require operator queueing.
Open Pstack lands on green: Mergify merges a ready PR to main once verify and exact-head live-gate pass and no review thread is unresolved, and a workflow resolves Unfret's own threads. A PR that touches `.github/` or the Mergify config also waits for Unfret, and workflow changes require operator queueing.
This port applies the Cursor → Claude Code substitutions in skill bodies. Earlier drafts left them flagged; this revision resolves them. A later pass added a Codex build that shares the same skills; see [Codex port](#codex-port) below.

## 1.16.0 syncs to Cursor pstack 0.15.29
Expand Down
5 changes: 3 additions & 2 deletions REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ This repository has two surfaces. Shipped plugin code, `plugins/pstack/**`, runs
- Contract (`.claude/skills/verify-open-pstack/scripts/harness.ts`, `features/recipes.ts`): the verifier names each fixture directory `state/<harness>/fixtures/<feature slug>` inside the run root, and generates per-run fixture markers with `crypto.randomUUID()`.
- Contract (`plugins/pstack/skills/poteto-mode/references/provider-dispatch.md`): it names no CLI version range. A runner lane requires the assigned provider CLI installed and authenticated as the launcher's preflight checks, a parent sandbox that lets that CLI reach its network and state, and, on Linux, Landlock and `bwrap` for Grok's bounded profiles. A lane that lacks one of these ends as a named dropout or a failed receipt, never a fallback.
- Contract (`docs/reference.md`, Dependencies): shipped tooling uses `gh` (or Origin's `origin`), `bun`, `node`, and the provider CLIs in the user's model sheet; `gt` only for the Orchestrate playbook; `jq` and `rg` only for `worktree-audit.sh`, which warns without them.
- Contract (`.claude/skills/verify-open-pstack/features/recipes.ts`, runner and Reflect assertions): the live gate's evidence is the harness-recorded command naming the installed tool, plus a receipt or output that carries this run's paths, markers or results. It guards against model claims; it does not attest that a file executed. Operator policy (2026-10-11, PR #151 step-back decision): this is the gate's evidence standard. A false gate decision that an honest run can reach stays material.
- Contract (`.claude/skills/verify-open-pstack/features/recipes.ts`, runner and Reflect assertions): the live gate's evidence is the harness-recorded command naming the installed tool, plus a receipt or output that carries this run's paths, markers or results. It guards against model claims; it does not attest that a file executed. Operator policy (2026-10-11, PR #151 step-back decision): this is the gate's evidence standard. Operator policy (2026-10-21, #240): an assertion the live gate publishes that can pass without the behavior it names is material, and its correction is to delete the assertion and list the behavior under the PR's Not verified. A behavior no recipe asserts is a coverage gap: an advisory listed under Not verified.
- Contract (`.mergify.yml`; operator policy 2026-10-21, #241): this repository lands on green. Only a pull request that touches `.github/**` or the Mergify config waits for `Unfret` before it merges, and the verifier, `.claude/skills/verify-open-pstack/**`, is not a risky path. Unfret reviews every other head in parallel, usually after the merge, and a material finding on a merged head gets a fix-forward pull request.

## Entry-point scope (read by the review panel as a mandate)

Expand All @@ -41,7 +42,7 @@ Each scoped sentence in this section names a concrete entry point or bounded sur
- `verify.sh doctor` and `verify.sh run` serve only the operator's Mac, which `doctor.ts` and `harness.ts` enforce as Darwin; a live-gate failure that needs a non-Darwin host is out of scope, but the verifier's unit tests and typecheck in Ubuntu CI stay in scope.
- The verifier's `--output` run root is `$SESSION/live` in a directory the documented launch recipe makes with `mktemp -d` under the operator's `TMPDIR`, and the verifier refuses an existing path; a failure that needs a hand-named run root is out of scope, but any path an inherited `TMPDIR` gives stays in scope.
- Fixture directories and per-run markers in `.claude/skills/verify-open-pstack/**` are generated by `harness.ts` and `features/recipes.ts`; a failure that needs a fixture or marker name chosen by hand is out of scope.
- The live gate in `.claude/skills/verify-open-pstack/**` must reject model claims and reach a correct gate decision on every honest run; a failure that needs the model under test to deliberately forge a tool's output, such as rebuilding a runner or resolver result with other commands, is out of scope.
- The live gate in `.claude/skills/verify-open-pstack/**` must reject model claims, and every assertion it publishes must be backed by its retained evidence; a failure that needs the model under test to deliberately forge a tool's output, such as rebuilding a runner or resolver result with other commands, is out of scope.
- In `.claude/skills/verify-open-pstack/**`, a failure that needs the model under test to deliberately evade a recipe, such as a crafted shell spelling, a value hidden in a variable or a report echoed without its lookup, is out of scope even when it would change the gate decision; a wrong decision on an honest run stays in scope.
- In `plugins/pstack/**`, a failure that needs a host missing a prerequisite named in `docs/reference.md` (Dependencies) or `provider-dispatch.md` (Host and parent prerequisites) is out of scope, except for the behavior those files promise on such a host.
- On such a host, `plugins/pstack/**` must keep the promised behavior: `worktree-audit.sh` warns without `jq` or `rg`, a blocked external CLI is a loud dropout, and a Grok lane without Landlock or `bwrap` ends `child-failed` with Grok's refusal in its receipt.
Expand Down
Loading
Loading