Skip to content
Open
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
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
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.

## Unreleased

**GPT-6 Astra lanes.** The model catalog registers `codex:gpt-6-astra` as an optional family with efforts `low` to `ultra`, as `codex debug models` lists them in Codex CLI 0.164.0, and a default of `max`. Astra is the second family after Sol that offers `ultra`. Codex families have no generated Claude agents, so a Claude Code parent runs Astra through the external runner. Default roles and panels are unchanged.

## 1.16.0 syncs to Cursor pstack 0.15.29

Open Pstack 1.16.0 tracks Cursor pstack 0.15.29 at `88d47514b82e7c44f00f2b2a72ff6dd12158038c`. Sixteen upstream commits since 0.15.25 touch pstack. Nine change skill content, and all nine are applied with the port adaptations below. `a968159` names two skills after their folders: the port's `poteto-mode` already complies, and `make-bot-ui` stays out. Three add upstream's own Claude Code install and a docs check, which stay out apart from their README text. Three bump only the Cursor manifests and guide, which stay out. The release also fixes #46 and #235, and ships Claude Haiku 5.5 lanes (#223), foreground waits on Claude Code (#232, #233) and the comment sweep (#218), all merged after 1.15.0. Default roles and panels are unchanged.
Expand Down
2 changes: 1 addition & 1 deletion docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ The Codex build shares one `skills/` tree with the Claude Code build. Nothing is
- **Tool and built-in mapping.** Claude tool names and built-in skills resolve through [`codex-tools.md`](../plugins/pstack/skills/poteto-mode/references/codex-tools.md). Model execution resolves separately through [`provider-dispatch.md`](../plugins/pstack/skills/poteto-mode/references/provider-dispatch.md), so Codex models stay native while other providers run through the external runner.
- **Subagents.** The `Agent` tool maps to Codex `spawn_agent` / `wait_agent`, enabled by `multi_agent = true`. Parallel fan-out is multiple `spawn_agent` calls in one turn. If the native Codex lane is unavailable, record that lane as a dropout; the configured external lanes still run, and no provider is silently substituted. There is no `poteto-agent` subagent type on Codex; route ad-hoc subagents by dispatching a `spawn_agent` told to read `poteto-mode` first.
- **Auto-fire.** The `hooks/` SessionStart injection is Claude Code-only; Codex has no plugin hook runtime. Enter `pstack:poteto-mode` by name, or add a standing instruction to `~/.codex/AGENTS.md` if you want the same always-on routing.
- **Models.** `/setup-pstack` writes provider-qualified descriptors and asks one requested effort per assigned family (`low`, `medium`, `high`, `xhigh`, `max`, and `ultra` where the model's CLI lists it, today only Sol). The first-run sheet is the model catalog's [role defaults](../plugins/pstack/skills/poteto-mode/references/model-catalog.md#role-defaults), with a panel of Opus, Sol, and Grok; Fable, Sonnet, Luna, Haiku 5.5 and an exact Opus pin stay selectable. Fable, Opus and Sonnet use Claude's rolling aliases; a registered exact pin keeps its own model. Runtime dispatch normalizes older versioned descriptors in memory, so an installed sheet stops pinning immediately. A setup rerun persists that migration while keeping each role's family and effort. In Codex, Codex families such as Sol use native `spawn_agent`; every other provider uses the deterministic external runner. In Claude Code, Claude families use native agents; every other provider uses the runner. A Codex writer that the runner launches stays in Codex's `workspace-write` sandbox unless the sheet opts in with the line `codex writer sandbox: full-access`, which setup asks about once. Full access lets the writer launch a browser and write dot-folders such as `.agents/`, and removes every sandbox limit. Children never detect the parent or reroute themselves. The `bug-fix`, `perf-issue`, and `hillclimb` roles stay on Sol instead of upstream's Grok default.
- **Models.** `/setup-pstack` writes provider-qualified descriptors and asks one requested effort per assigned family (`low`, `medium`, `high`, `xhigh`, `max`, and `ultra` where the model's CLI lists it, today Sol and Astra). The first-run sheet is the model catalog's [role defaults](../plugins/pstack/skills/poteto-mode/references/model-catalog.md#role-defaults), with a panel of Opus, Sol, and Grok; Fable, Sonnet, Luna, Astra, Haiku 5.5 and an exact Opus pin stay selectable. Fable, Opus and Sonnet use Claude's rolling aliases; a registered exact pin keeps its own model. Runtime dispatch normalizes older versioned descriptors in memory, so an installed sheet stops pinning immediately. A setup rerun persists that migration while keeping each role's family and effort. In Codex, Codex families such as Sol use native `spawn_agent`; every other provider uses the deterministic external runner. In Claude Code, Claude families use native agents; every other provider uses the runner. A Codex writer that the runner launches stays in Codex's `workspace-write` sandbox unless the sheet opts in with the line `codex writer sandbox: full-access`, which setup asks about once. Full access lets the writer launch a browser and write dot-folders such as `.agents/`, and removes every sandbox limit. Children never detect the parent or reroute themselves. The `bug-fix`, `perf-issue`, and `hillclimb` roles stay on Sol instead of upstream's Grok default.

Verified in fresh installed Claude Code and Codex sessions: the user-facing skills are discovered and namespaced under `pstack`; both parents fan out the configured panel through the documented native/external route table, retain long-running handles without a default timeout, and cross-judge only after every candidate is terminal. The `principle-*` leaves remain available for `poteto-mode` to read by path. Claude honors their `user-invocable: false` metadata; Codex 0.149.0 does not ([#8](https://github.com/ericlitman/open-pstack/issues/8)).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Model and provider facts for [`provider-dispatch.md`](provider-dispatch.md), whi
| grok | Grok 4.7 | grok | grok-4.7 | xhigh | low medium high xhigh max | - | default | grok-4.7-xhigh-fast |
| opus | Opus | claude | opus | max | low medium high xhigh max | `pstack-opus-<effort>` | default | opus |
| luna | GPT-6 Luna | codex | gpt-6-luna | max | low medium high xhigh max | - | optional | - |
| astra | GPT-6 Astra | codex | gpt-6-astra | max | low medium high xhigh max ultra | - | optional | - |
| sonnet | Sonnet | claude | sonnet | max | low medium high xhigh max | `pstack-sonnet-<effort>` | optional | - |
| opus-5-5 | Opus 5.5 | claude | claude-opus-5-5 | max | low medium high xhigh max | `pstack-opus-5-5-<effort>` | optional | claude-opus-5-5-xhigh |
| haiku-5-5 | Haiku 5.5 | claude | claude-haiku-5-5 | max | low medium high xhigh max | `pstack-haiku-5-5-<effort>` | optional | - |
Expand Down
15 changes: 12 additions & 3 deletions plugins/pstack/skills/poteto-mode/scripts/runner/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,14 @@ describe("registry data", () => {
}
});

it("keeps the default efforts, the Claude rolling aliases and Sol's ultra", () => {
it("keeps the default efforts, the Claude rolling aliases, and ultra on Sol and Astra", () => {
expect(REGISTRY.families.map((entry) => [entry.name, entry.defaultEffort])).toEqual([
["fable", "max"],
["sol", "max"],
["grok", "xhigh"],
["opus", "max"],
["luna", "max"],
["astra", "max"],
["sonnet", "max"],
["opus-5-5", "max"],
["haiku-5-5", "max"],
Expand All @@ -111,8 +112,8 @@ describe("registry data", () => {
.map((entry) => [entry.name, entry.rolling!.revisionPrefix]))
.toEqual([["fable", "claude-fable-"], ["opus", "claude-opus-"], ["sonnet", "claude-sonnet-"]]);
expect(REGISTRY.families.filter((entry) => entry.provider === "codex").map((entry) => [entry.name, entry.model]))
.toEqual([["sol", "gpt-6.1-sol"], ["luna", "gpt-6-luna"]]);
expect(REGISTRY.families.filter((entry) => effortsOf(entry).includes("ultra")).map((entry) => entry.name)).toEqual(["sol"]);
.toEqual([["sol", "gpt-6.1-sol"], ["luna", "gpt-6-luna"], ["astra", "gpt-6-astra"]]);
expect(REGISTRY.families.filter((entry) => effortsOf(entry).includes("ultra")).map((entry) => entry.name)).toEqual(["sol", "astra"]);
});

it("keeps family names and (provider, model) pairs unique, and efforts ordered", () => {
Expand Down Expand Up @@ -463,6 +464,14 @@ describe("resolveLane", () => {
.toThrow("effort ultra is not offered for claude:sonnet");
});

it("resolves Astra at max by default and passes ultra to Codex", () => {
const astra = resolveLane(request({ parent: "claude", provider: "codex", model: "gpt-6-astra" }));
expect([astra.family?.name, astra.effort]).toEqual(["astra", "max"]);
const ultra = resolveLane(request({ parent: "claude", provider: "codex", model: "gpt-6-astra", effort: "ultra" }));
expect(ultra.adapter.launch.argv(ultra, { sharedGitDir: null, providerSidecar: null }))
.toEqual(expect.arrayContaining(["gpt-6-astra", 'model_reasoning_effort="ultra"']));
});

it("maps each Devin effort to its CLI model ID and keeps the descriptor's model for the receipt", () => {
const devin = (model: string, effort: string, mode = "read-only") =>
resolveLane(request({ parent: "claude", provider: "devin", model, effort, mode }));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ export const FAMILIES = [
defaultEffort: "max",
upstream: null,
}),
family({
name: "astra",
label: "GPT-6 Astra",
provider: "codex",
model: "gpt-6-astra",
efforts: UP_TO_ULTRA, // `codex debug models`, Codex CLI 0.164.0
defaultEffort: "max",
upstream: null,
}),
family({
name: "sonnet",
label: "Sonnet",
Expand Down Expand Up @@ -209,4 +218,5 @@ export const RELEASED_PAIRS = [
{ pair: "cursor:composer-2.5", family: "composer-2.5" },
{ pair: "cursor:gemini-3.7-flash", family: "gemini-3.7-flash" },
{ pair: "claude:claude-haiku-5-5", family: "haiku-5-5" },
{ pair: "codex:gpt-6-astra", family: "astra" },
] as const satisfies readonly ReleasedPair<FamilyName>[];