From ded1b16ec14ce9186714ade8aec483c5b516cd10 Mon Sep 17 00:00:00 2001 From: Arjit Jaiswal Date: Thu, 1 Oct 2026 01:00:07 -0400 Subject: [PATCH 1/4] Offer explicit recovery choices during setup --- .../verify-open-pstack/features/setup.md | 2 ++ .claude-plugin/marketplace.json | 2 +- CHANGELOG.md | 6 ++++++ UPSTREAM.md | 2 +- plugins/pstack/.claude-plugin/plugin.json | 2 +- plugins/pstack/.codex-plugin/plugin.json | 2 +- .../references/provider-dispatch.md | 2 +- plugins/pstack/skills/setup-pstack/SKILL.md | 18 +++++++++++++++--- tests/setup-selected-providers.md | 3 +++ 9 files changed, 31 insertions(+), 8 deletions(-) diff --git a/.agents/skills/verify-open-pstack/features/setup.md b/.agents/skills/verify-open-pstack/features/setup.md index 7e1fe90..6af0dd3 100644 --- a/.agents/skills/verify-open-pstack/features/setup.md +++ b/.agents/skills/verify-open-pstack/features/setup.md @@ -6,6 +6,7 @@ Setup discovers available access, recommends role assignments, and saves approve - `setup-selected`: check only providers selected for the final assignments. - `setup-persist`: confirm and read back the model sheet and parent integration. +- `setup-recovery`: explicit fallback modes and independent same-route continuation choices on first setup and reruns. - `setup-failure`: preserve existing configuration on failed validation or writes. ## How to get to it (user POV) @@ -20,6 +21,7 @@ Preconditions: Installation recipe passed in the parent being tested; dedicated - **Invoke:** enter `Use pstack:setup-pstack. Configure only inherit-parent roles, with two independent architect runners. Do not select external providers.` in the test parent. This selects the native-only scenario, not permission to rewrite a personal sheet. - **Drive:** answer access questions from known facts, review the proposed assignments, and confirm saving the disposable test configuration. Observe a real native marker probe, a worker, and a separate reviewer; capture native dispatch and terminal events, not just their summary text. - **Read back:** compare saved sheet and integration with the confirmed proposal. Require all documented role rows, two architect seats, and no external CLI probes for the native-only case. In Codex the instructions file is `AGENTS.md`; Claude uses `CLAUDE.md`. Record the actual paths chosen by setup. +- **Recovery choices:** drive cases 17 and 18 in `tests/setup-selected-providers.md` in each parent. Capture the presented choices, current mode, accepted choice, confirmation, and independently read saved policy. Exercise broad, quota-only, disabled, and preserved custom fallback policies; independently opt in, keep, and disable continuation. Do not count synthetic policy events as real provider failure or continuation proof. - **Failure paths:** follow the named selected-provider failure and write-failure cases in `tests/setup-selected-providers.md`; retain before/after bytes and expect rollback. Record cases not exercised separately. ## Gotchas diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 8a8c9a5..4f6f791 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ "name": "pstack", "source": "./plugins/pstack", "description": "if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence.", - "version": "1.10.2", + "version": "1.10.3", "author": { "name": "Lauren Tan (original)" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index cf14186..32149af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ This file records what each version of the Open Pstack package changed. Versions Entries describe package versions. Published release checkpoints have tag links; installation follows `main` unless pinned. Validation belongs to the linked pull requests. Older reports remain available through immutable links. +## 1.10.3 makes setup recovery choices explicit + +Cursor baseline: [0.15.5](https://github.com/cursor/plugins/tree/12d587dfb20741cafc376c42c696c5f6e2a64487/pstack). [Issue #108](https://github.com/arjitj2/open-pstack/issues/108). + +Setup offers automatic backend recovery, usage exhaustion only, or no automatic fallback on first setup and reruns. Same-model continuation is a separate opt-in choice. Existing policies stay unchanged until accepted, and confirmation shows both recovery settings. + ## 1.10.2 enables worker local checks Cursor baseline: [0.15.5](https://github.com/cursor/plugins/tree/12d587dfb20741cafc376c42c696c5f6e2a64487/pstack). [Issue #103](https://github.com/arjitj2/open-pstack/issues/103). [PR #104](https://github.com/arjitj2/open-pstack/pull/104). Tag [v1.10.2](https://github.com/arjitj2/open-pstack/releases/tag/v1.10.2). diff --git a/UPSTREAM.md b/UPSTREAM.md index 80cc310..873087b 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -12,7 +12,7 @@ This page records the current Cursor baseline and the maintainer procedure for r | Path | `pstack/` | | Commit | `12d587dfb20741cafc376c42c696c5f6e2a64487` | | Upstream version | `0.15.5` | -| open-pstack version | `1.10.2` | +| open-pstack version | `1.10.3` | The table records the packaged version on `main` and the Cursor content it incorporates, minus the exclusions below. Detecting or reviewing a newer Cursor commit does not advance this baseline. Cursor's version identifies the imported content. The open-pstack version identifies the cross-harness package, and its numbers are independent of Cursor and Eric's port. diff --git a/plugins/pstack/.claude-plugin/plugin.json b/plugins/pstack/.claude-plugin/plugin.json index 6f70b93..50f3c83 100644 --- a/plugins/pstack/.claude-plugin/plugin.json +++ b/plugins/pstack/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "pstack", "displayName": "pstack", - "version": "1.10.2", + "version": "1.10.3", "description": "if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence. Ported from cursor/plugins/pstack for Claude Code and Codex.", "author": { "name": "Lauren Tan" diff --git a/plugins/pstack/.codex-plugin/plugin.json b/plugins/pstack/.codex-plugin/plugin.json index 97fa7fd..6af00c3 100644 --- a/plugins/pstack/.codex-plugin/plugin.json +++ b/plugins/pstack/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "pstack", - "version": "1.10.2", + "version": "1.10.3", "description": "if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence. Codex port of the Claude Code plugin; skills are shared, tool names resolve via skills/poteto-mode/references/codex-tools.md.", "author": { "name": "Lauren Tan" diff --git a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md index 79b2a55..ff52d94 100644 --- a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md +++ b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md @@ -264,7 +264,7 @@ The runner and native tool envelopes classify failures; the helper owns every ro # fallback: {"on":["usage-exhausted","route-unavailable","terminal-failure","deadline-exceeded"]} ``` -The `on` list is a closed union over exactly those four policy outcomes, with no duplicates and no unknown keys; a second `# fallback:` line anywhere in the sheet (preamble, between rows, or footer) is malformed and rejected. A sheet without the line keeps quota-only behavior — `{"on":["usage-exhausted"]}` — so existing sheets advance only on proven exhaustion. Declaring the line makes the sheet policy-enabled in the same sense as saved access facts and chains: required role rows must be present rather than defaulting, and every configured route needs an access fact. The policy declares which terminal outcomes *may* advance; it never adds an attempt, a provider, a model, an effort, or an `apiSpend` approval beyond what the saved chain already authorizes. Setup may recommend a broader policy on a new sheet and must show its effect in the candidate; it never edits an existing sheet's policy without explicit acceptance. +The `on` list is a closed union over exactly those four policy outcomes, with no duplicates and no unknown keys; a second `# fallback:` line anywhere in the sheet (preamble, between rows, or footer) is malformed and rejected. An explicit `# fallback: {"on":[]}` disables automatic fallback while retaining any saved chains. Removing the line does not disable fallback. A sheet without the line keeps quota-only behavior — `{"on":["usage-exhausted"]}` — so existing sheets advance only on proven exhaustion. Declaring the line makes the sheet policy-enabled in the same sense as saved access facts and chains: required role rows must be present rather than defaulting, and every configured route needs an access fact. The policy declares which terminal outcomes *may* advance; it never adds an attempt, a provider, a model, an effort, or an `apiSpend` approval beyond what the saved chain already authorizes. Setup may recommend a broader policy on a new sheet and must show its effect in the candidate; it never edits an existing sheet's policy without explicit acceptance. The receipt boundary is deterministic: `bun scripts/model-policy/pstack-model-policy normalize --sheet --role --parent

--lane --attempt --receipt --mode [--contract ]` reads the actual runner receipt, checks that its parent/provider/model/effort match the frozen sheet's attempt `i` on lane `n`, that its recorded access mode equals the requested `--mode`, its contract equals `--contract` (legacy by default), and its recorded `apiSpend` equals the attempt's saved access fact, checks internal consistency (a not-started receipt can only fail in preflight, a preflight failure cannot be started, postprocess requires a clean exit), and prints the policy `AttemptOutcome` (plus the receipt path as evidence) or exits nonzero. Agents never reimplement the mapping. The total mapping over receipt statuses: diff --git a/plugins/pstack/skills/setup-pstack/SKILL.md b/plugins/pstack/skills/setup-pstack/SKILL.md index 578bda5..e9503fa 100644 --- a/plugins/pstack/skills/setup-pstack/SKILL.md +++ b/plugins/pstack/skills/setup-pstack/SKILL.md @@ -78,10 +78,22 @@ Show the normalized complete role map, the model matrix, and this parent's route Why and Reflect require the parent's live MCP surface. Keep their investigator, reviewer, and synthesizer roles on `inherit-parent` or `auto`. Preserve all documented role rows, and preserve the order of untouched lanes. Say when replacements or removals reduce provider diversity. If the operator already named role changes, apply those without asking them to repeat the choice. -When recommending a mix, reason from confirmed facts: task fit, included or reported capacity before metered spend, the requested reasoning budget, required MCP access, and panel provider diversity. Suggest concrete ordered fallbacks where they add real coverage — a second provider's included capacity behind a constrained primary — and explain what each fallback changes in quality or provider mix. Every fallback must name an exact `provider:model@effort` descriptor, `inherit-parent`, or `auto`; never a different model on the same provider's current CLI account, and at most three attempts per seat. On a new sheet, recommend the broader `# fallback: {"on":["usage-exhausted","route-unavailable","terminal-failure","deadline-exceeded"]}` recovery policy or a justified subset — it lets a declared backend outage advance the same saved chain instead of dropping the seat — and show in the candidate exactly which outcomes the saved policy would recover and which still stop (cancelled, billing-blocked, ordinary task failure, and every unauthorized route always stop). An existing sheet's policy line, including its absence (quota-only), is preserved verbatim unless the operator explicitly accepts a change. Preserve existing customized assignments, efforts, lane order, and chains unless the operator accepts a change. Why and Reflect stay on `inherit-parent` or `auto`; an MCP-less external lane is not an equivalent substitute there. +When recommending a mix, reason from confirmed facts: task fit, included or reported capacity before metered spend, the requested reasoning budget, required MCP access, and panel provider diversity. Suggest concrete ordered fallbacks where they add real coverage — a second provider's included capacity behind a constrained primary — and explain what each fallback changes in quality or provider mix. Every fallback must name an exact `provider:model@effort` descriptor, `inherit-parent`, or `auto`; never a different model on the same provider's current CLI account, and at most three attempts per seat. Choose recovery behavior separately in step 4a. An existing sheet's policy line, including its absence (quota-only), is preserved verbatim unless the operator explicitly accepts a change. Preserve existing customized assignments, efforts, lane order, and chains unless the operator accepts a change. Why and Reflect stay on `inherit-parent` or `auto`; an MCP-less external lane is not an equivalent substitute there. Derive the assigned family set from the resulting role descriptors, excluding `inherit-parent` and `auto`. An unassigned family is optional: do not ask for its effort, check its CLI or credentials, or probe it. A missing Grok CLI cannot block a configuration with no Grok roles. Availability checks must not choose assignments for the operator. +### 4a. Choose automatic recovery + +On first setup and reruns, show the current fallback mode, including usage exhaustion only when no `# fallback` line exists. Ask which mode to use, with these choices and effects. Reuse an explicit choice already made in this conversation instead of asking again; an unanswered question preserves the current policy. Recommend automatic recovery for a new sheet, but do not enable it without acceptance. + +- **Automatic recovery (recommended).** Use the next saved fallback when usage is exhausted, a route is unavailable, the backend terminates unsuccessfully, or an explicitly configured deadline expires. Save `# fallback: {"on":["usage-exhausted","route-unavailable","terminal-failure","deadline-exceeded"]}`. +- **Usage exhaustion only.** Use the next saved fallback only on proven exhaustion. Save `# fallback: {"on":["usage-exhausted"]}` when changing modes; preserve an existing quota-only declaration or its absence when keeping that mode. +- **No automatic fallback.** Do not advance to another descriptor after a failed attempt. Save `# fallback: {"on":[]}`; removing the line would restore quota-only behavior. Preserve saved chains so they remain available if the operator later enables fallback. + +For an existing custom subset, show its actual triggers and allow keeping it verbatim rather than forcing it into one of these presets. Explain that each mode uses only the exact saved chains; a seat without a backup gains no additional provider. Changing recovery never changes models, efforts, access or spending permission, and never adds a timeout. A completed response reporting failing project tests is task output, not a backend failure. Cancellation, billing blocks, safety denials, and unsupported capabilities stop under every fallback mode. A verified permission blockage cannot switch providers. Refer to [saved fallback and backend recovery](../poteto-mode/references/provider-dispatch.md#saved-fallback-and-backend-recovery) for receipt classification and writer inspection requirements. + +Ask a separate continuation question on first setup and reruns, showing the current `# continuation` policy or its absence. Offer **Resume the same model after a corrected permission blockage or completed parent handoff** with `# continuation: {"on":["permission","handoff"],"max":2}`, or **Stop without automatic continuation**, represented by no continuation line. The limit is two total executions per descriptor, including the original, not two retries. Keep the current choice unless the operator explicitly changes it; reuse a choice already made and preserve custom causes or limits verbatim when keeping them. Explain that continuation requires parent inspection of partial work and side effects, a concrete corrected blockage or completed operation, stopped prior writers, and a fresh execution. It never bypasses a denial or switches models. Do not infer continuation consent from accepting automatic fallback. Show both selected policies again at confirmation. + ### 5. Validate and choose assigned efforts Every non-alias value must match `:@` on a supported provider. Apply the optional Cursor, Antigravity, and OpenCode rules above to their descriptors. The model matrix supplies recommended defaults and effort guidance, never an allowlist: the model is the exact ID the provider's CLI accepts, not a matrix row. Where the CLI advertises a model list — `grok models`, `devin models list --format json` (`variants[].model_uid` only), `cursor-agent models`, `agy models`, `opencode models` — choose the ID from it, remembering that a listing is evidence, not entitlement. Use current host capabilities for Claude and Codex where exposed, and let the step 6 probe prove an explicitly selected alias or exact model ID. Effort stays per provider: `low` through `max` for Claude, Codex, and Grok; fixed `default` for Cursor, Antigravity, OpenCode, and Devin UIDs, whose effort is part of the ID; the legacy `devin:swe-2@medium|high|max` and `devin:swe-1.6@default` mappings unchanged. Matrix rows keep their Selectable efforts guidance for the listed families. An invalid descriptor, out-of-domain effort, duplicate role, or unknown role is inconsistent state; never mutate an unknown model into a matrix family name or substitute a different model for the selected one. Show the conflicting rows verbatim and resolve them through an explicit provider-qualified descriptor or alias replacement before probing or writing. @@ -117,13 +129,13 @@ Receipts and native transcripts prove the requested effort and the route. They d ### 7. Render the selected role map -Build the new sheet in memory from the complete role map selected in step 4 and the requested efforts from step 5. Record the chosen budget as a `# budget: