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

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.

## PR 59 setup changes awaiting installed verification

Setup accepts optional Astra and explicitly loaded provider extensions while retaining the default four-family panel. Native Codex dispatch preserves the selected model and effort with a compatible history setting. Claude output parsing supports terminal results inside event arrays. Grok preflight recognizes API-key authentication, verifies the exact model token, and rejects explicit unavailable-model responses before execution.

This branch includes the 1.4.1 upstream sync below. Its changed setup behavior still requires installed live verification in both harnesses before readiness.

## 1.4.1 syncs to Cursor pstack 0.15.1

Open Pstack 1.4.1 tracks Cursor pstack 0.15.1 at `f8abeddd1862dc73704e3d719dd73df0d51b8c71`. Poteto-mode now requires each claim to include its evidence or a measured, inferred, or guess label in the same sentence. Agents also run any check they can run themselves instead of handing that check to the user. No playbook, model, runtime, or dependency changed.
Expand All @@ -18,6 +24,7 @@ Opening a PR adopts the "briefing, not the lab notebook" guidance and links deta

Existing exclusions remain: `make-bot-ui`, Benny automations, Cursor-only guide and sticky-mode content, Cursor-only solo-model defaults, invocation-blocking flags on How, Why, Unslop, and TypeScript best practices, and the unsupported Claude manifest logo field. Those four skills stay user-invocable and model-invocable. The principle leaves keep `user-invocable: false`, which hides them from the slash menu but leaves model invocation open. The Cursor manifest is not imported. Watcher and orchestrator directories, package metadata, and lockfile have no upstream changes in this range. The unrelated Grok Voice plugin remains outside this sync.


## 1.3.0 syncs to Cursor pstack 0.14.7

Open Pstack now tracks Cursor pstack 0.14.7 at `efa2a531985e0a8084d36ff3cf87233be8a9f34b`.
Expand Down
5 changes: 3 additions & 2 deletions plugins/pstack/skills/poteto-mode/references/codex-tools.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Codex tool mapping for pstack

pstack skills retain Claude Code tool language (`Skill`, `Agent`, `AskUserQuestion`) in shared prose. On Codex the files are the same; only those tool names resolve differently. Model execution is not translated here. Read [`provider-dispatch.md`](provider-dispatch.md) for the parent-owned Claude/Codex/Grok route table and provider-qualified descriptors.
pstack skills retain Claude Code tool language (`Skill`, `Agent`, `AskUserQuestion`) in shared prose. On Codex the files are the same; only those tool names resolve differently. Model execution is not translated here. Read [`provider-dispatch.md`](provider-dispatch.md) for the parent-owned built-in and optional extension routes and provider-qualified descriptors.

## Tool actions

Expand Down Expand Up @@ -35,14 +35,15 @@ Without it, the native Codex lane is a named dropout. Independent external lanes
poteto-mode's Subagents section sets Claude-specific defaults (`subagent_type: "poteto-agent"`, `run_in_background: true`). On Codex:

- There is no `poteto-agent` subagent type. Route an ad-hoc subagent through poteto-mode's style by dispatching a `spawn_agent` whose instructions tell it to read the `poteto-mode` skill in full first.
- Model or effort overrides use `fork_turns: "none"` and a complete task with grounding paths; full-history forks inherit the parent and cannot carry these overrides.
- `spawn_agent` calls already run concurrently with your turn, so `run_in_background: true` has no separate flag. Issue the dispatch and continue.
- There is no `comment-sicko` subagent type either. The **no-comments** skill spawns it on Claude Code; on Codex dispatch a `spawn_agent` whose instructions tell it to read `agents/comment-sicko.md` in full first.
- Claude Code runs every subagent on this machine, so the **swarm** skill's workers and the fan-out playbooks (`orchestrate`, `autopilot-full`, `autopilot-stack`) isolate writers with worktrees. The same holds on Codex.
- Keep the rest of the policy unchanged. Pass file pointers not inlined context, give each worker its own worktree or branch when they write, review every subagent's diff yourself.

## Models and providers

Do not replace every configured entry with a Codex model. `/setup-pstack` writes portable descriptors such as `claude:fable@max`, `codex:gpt-5.6-sol@max`, and `grok:grok-4.6@xhigh`. In a Codex parent, only `codex:*` is native. Route Claude and Grok descriptors through the external launcher exactly as `provider-dispatch.md` specifies. The current default panel intentionally keeps four-provider frontier diversity and contains no older GPT or Claude substitute.
Do not replace every configured entry with a Codex model. `/setup-pstack` writes portable descriptors such as `claude:fable@max`, `codex:gpt-5.6-sol@max`, and `grok:grok-4.6@xhigh`. In a Codex parent, only `codex:*` is native. Route Claude and Grok descriptors through the external launcher exactly as `provider-dispatch.md` specifies. The default panel has four model families across three providers. Optional Astra is also native. If the sheet contains `Dispatch extension: <path>`, read it before validating or dispatching extension descriptors and invoke its declared launcher directly. The sheet remains the only role configuration; the extension supplies routes and evidence. User choices and effort caps override examples.

## Claude built-in skills pstack references

Expand Down
49 changes: 41 additions & 8 deletions plugins/pstack/skills/poteto-mode/references/provider-dispatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,50 @@ pstack model choices are provider-qualified descriptors:

## Model matrix

| Family | Upstream pstack choice | Provider | Model | Default effort | Selectable efforts | Claude-native agent stem |
|---|---|---|---|---|---|---|
| fable | fable | claude | fable | max | low medium high xhigh max | fable |
| sol | gpt-5.6-sol-max | codex | gpt-5.6-sol | max | low medium high xhigh max | - |
| grok | grok-4.6-fast-xhigh | grok | grok-4.6 | xhigh | low medium high xhigh max | - |
| opus | opus | claude | opus | xhigh | low medium high xhigh max | opus |
| Family | Upstream pstack choice | Provider | Model | Default effort | Selectable efforts | Claude-native agent stem | First-run active |
|---|---|---|---|---|---|---|---|
| fable | fable | claude | fable | max | low medium high xhigh max | fable | yes |
| sol | gpt-5.6-sol-max | codex | gpt-5.6-sol | max | low medium high xhigh max | - | yes |
| grok | grok-4.6-fast-xhigh | grok | grok-4.6 | xhigh | low medium high xhigh max | - | yes |
| opus | opus | claude | opus | xhigh | low medium high xhigh max | opus | yes |
| astra | - | codex | gpt-6-astra | medium | low medium high xhigh max | - | no |

The allowed effort universe is exactly `low`, `medium`, `high`, `xhigh`, `max`. First-run requested efforts are the Default effort cell of each row. A Claude-native agent stem of `-` means the family has no Claude-native agent. Otherwise the shipped agent name is `pstack-<stem>-<effort>`.
The allowed effort universe is exactly `low`, `medium`, `high`, `xhigh`, `max`. The default panel selects Fable, Sol, Grok, and Opus in that order. Rows with First-run active `yes` seed a missing sheet; otherwise derive selected families from the loaded role map and explicit user changes. Optional families are selected only by a loaded role descriptor or an explicit user choice. Defaults propose effort only for selected families without a supplied or current value. Explicit user model/effort choices override examples; user effort caps constrain every selection and probe, including loaded values. Never probe above a cap. Reuse choices already supplied in the conversation. A Claude-native agent stem of `-` means the family has no Claude-native agent. Otherwise the shipped agent name is `pstack-<stem>-<effort>`.

`fable` and `opus` are Claude Code's rolling aliases. Claude resolves each alias to the latest available family revision. A runner receipt keeps the requested alias in `model` and the concrete provider-reported revision in `reportedModel`; verification accepts only a numeric `claude-fable-*` or `claude-opus-*` revision from the matching family.

## Optional dispatch extensions

When the current model sheet contains a line `Dispatch extension: <path>`, read that reference before validating or dispatching any role. Setup may also load a path explicitly supplied by the user and persist this pointer in the sheet. Expand `~` for the current user; resolve relative paths against the model sheet directory. A missing reference is inconsistent state.

An extension declares additional provider/model families, selectable efforts, a proposed effort, parent-specific launcher argv, and authentication/model/completion evidence. It adds routing capabilities, never role assignments or a second mutable model configuration. Require unique families and provider/model pairs; extensions cannot override built-in families, routes, aliases, or the user's effort caps. Only families selected by a role descriptor or the user participate in setup. Persist all selected families in the role map.

The reference is UTF-8 JSON with `schemaVersion: 1` and a `families` array. This example describes the format; `example` and the executable path are illustrative placeholders, not an installed provider:

```json
{
"schemaVersion": 1,
"families": [{
"family": "example",
"provider": "example",
"model": "example-model",
"selectableEfforts": ["medium", "high"],
"defaultEffort": "medium",
"launcherArgv": {
"claude": ["/absolute/path/to/extension-runner", "--parent", "{parent}", "--provider", "{provider}", "--model", "{model}", "--effort", "{effort}", "--mode", "{mode}", "--prompt", "{promptPath}", "--cwd", "{cwd}", "--output", "{outputPath}", "--receipt", "{receiptPath}"],
"codex": ["/absolute/path/to/extension-runner", "--parent", "{parent}", "--provider", "{provider}", "--model", "{model}", "--effort", "{effort}", "--mode", "{mode}", "--prompt", "{promptPath}", "--cwd", "{cwd}", "--output", "{outputPath}", "--receipt", "{receiptPath}"]
},
"receiptSchema": "pstack-runner-v1"
}]
}
```

The parent validates the reference before invoking a launcher. Reject unknown fields, duplicate JSON keys, wrong types, empty families, built-in collisions, and duplicate families or provider/model pairs. Family, provider, and model names match `[a-z0-9][a-z0-9.-]*`. Efforts are unique members of the matrix's effort universe and include `defaultEffort`. `launcherArgv` has exactly `claude` and `codex` arrays of nonempty strings, beginning with an absolute executable path. Require each placeholder shown above exactly once per argv. Placeholders occupy a whole argument; embedded or unknown placeholders are invalid. Substitute each parent-owned value as one argv element, without shell evaluation or environment expansion. Only the extension reference path supports the tilde/relative-path rule above.

`receiptSchema` is exactly `pstack-runner-v1`: the launcher writes the existing `RunnerReceipt` fields in `scripts/runner/types.ts`, with `provider` set to its declared extension provider. It performs authentication/model preflight and records sanitized evidence, requested identity and effort, actual argv, exit code, model report, usage, and terminal status. Failed preflight must not execute the model. Success requires process exit 0, receipt `status: complete`, `preflight.status: passed`, `modelVerified: true`, matching parent/provider/model/effort/access/path fields, and nonempty output. Provider reports establish identity; `pinned-argv` is allowed only when the CLI does not report it and actual argv pins the exact model. Login alone or a child self-report is insufficient. The parent verifies the receipt against the assigned lane. Invalid or missing evidence fails setup without writing the sheet.

The parent routes an extension descriptor directly to its declared launcher, with the same unique paths, retained background handle, access boundary, and receipt checks as built-in external lanes. Read the launcher's help before first use. Never send an extension provider to the built-in runner or reinterpret it as a native model. Shared skills consume this contract through the model sheet; no runtime resolver or machine-specific dependency is required.
Comment thread
AojdevStudio marked this conversation as resolved.

## Read-time normalization

Normalize configured descriptors before matching them to the matrix or choosing a route. If a provider-qualified Claude model starts with `claude-fable-` or `claude-opus-` and its remaining revision contains only digits and hyphens, replace that model component in memory with `fable` or `opus`. Preserve provider, effort, role, and lane order. Use only the normalized descriptor for native dispatch or runner argv. Never pass the versioned predecessor to Claude.
Expand All @@ -43,7 +76,7 @@ The top-level harness resolves the route once. A child receives an assigned prov
Native dispatch avoids a second CLI startup and its base context.

- Claude Code: match the descriptor's `(provider, model)` to one model-matrix row, then dispatch it through `pstack-<stem>-<effort>` using that row's Claude-native agent stem and the descriptor's effort. Those definitions select the rolling model alias, requested effort, and `background: true`. `pstack-fable-max` and `pstack-opus-xhigh` remain in that set. Pass the complete task, grounding paths, access mode, and unique output location in the `Agent` prompt. Retain the task handle and drain it only after fan-out.
- Codex: call `spawn_agent` with the descriptor's model and `reasoning_effort`, the complete task, grounding paths, access mode, and unique output location. Use an isolated worktree for a writer. Codex subagents already run concurrently.
- Codex: call `spawn_agent` with the descriptor's model, `reasoning_effort`, and `fork_turns: "none"` (full-history forks cannot override model or effort), the complete task, grounding paths, access mode, and unique output location. Use an isolated worktree for a writer. Codex subagents already run concurrently.

Do not send a same-provider descriptor to the external runner. It rejects that call because the native route is cheaper and already available.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { EFFORTS, PARENTS, PROVIDERS } from "./types.ts";

const reference = readFileSync(
new URL("../../references/provider-dispatch.md", import.meta.url),
"utf8"
);

test("the documented extension example supplies each lane's complete argv and receipt contract", () => {
const section = reference.split("## Optional dispatch extensions")[1].split("## Read-time normalization")[0];
const example = JSON.parse(section.match(/```json\n([\s\S]*?)\n```/)![1]);
expect(example.schemaVersion).toBe(1);
expect(example.families).toHaveLength(1);
const family = example.families[0];
expect(PROVIDERS).not.toContain(family.provider);
expect(family.receiptSchema).toBe("pstack-runner-v1");
expect(new Set(family.selectableEfforts).size).toBe(family.selectableEfforts.length);
expect(family.selectableEfforts).toContain(family.defaultEffort);
for (const effort of family.selectableEfforts) expect(EFFORTS).toContain(effort);
expect(Object.keys(family.launcherArgv).sort()).toEqual([...PARENTS].sort());
const values: Record<string, string> = {
parent: "codex", provider: family.provider, model: family.model, effort: "medium",
mode: "read-only", promptPath: "/tmp/prompt with spaces.md", cwd: "/tmp/work with spaces",
outputPath: "/tmp/output.txt", receiptPath: "/tmp/receipt.json",
};
for (const parent of PARENTS) {
const argv: string[] = family.launcherArgv[parent];
expect(argv[0].startsWith("/")).toBe(true);
const placeholders = argv.filter((arg) => arg.startsWith("{"));
expect(placeholders.sort()).toEqual(Object.keys(values).map((key) => `{${key}}`).sort());
const substituted = argv.map((arg) => arg.startsWith("{") ? values[arg.slice(1, -1)] : arg);
expect(substituted[argv.indexOf("{promptPath}")]).toBe(values.promptPath);
expect(substituted[argv.indexOf("{cwd}")]).toBe(values.cwd);
expect(substituted).toHaveLength(argv.length);
}
});

test("setup requires an independent provider before any probe or write", () => {
const setup = readFileSync(new URL("../../../setup-pstack/SKILL.md", import.meta.url), "utf8");
const step2 = setup.split("### 2. Load current state")[1].split("### 3.")[0];
expect(step2).toContain("at least two distinct providers");
expect(step2).toContain("before probing or writing");
expect(setup).not.toContain("Require at least one selected family");
expect(setup.split("### 9. Behavioral smoke")[1]).toContain("independent cross-judge");
});
Loading