From 8251a88542bc6b04244f77136291c46c73d029f7 Mon Sep 17 00:00:00 2001 From: max-metaplanet Date: Fri, 6 Nov 2026 11:52:02 -0800 Subject: [PATCH 1/3] feat/no-ticket(runner): accept grok as a parent harness A Grok parent routes a matching Grok lane through spawn_subagent and every other lane, including a pinned Grok descriptor, through pstack-runner --parent grok. The generated catalog gains a Grok parent column. Claude and Codex still reject their own provider. The parent-by-adapter check is exhaustive over the finite registry. It is the property test for this table. There is no concurrency or permission state machine in the change, so there is no model-based test. --- CHANGES.md | 4 ++ README.md | 2 +- UPSTREAM.md | 2 + docs/reference.md | 2 +- plugins/pstack/skills/poteto-mode/SKILL.md | 4 +- .../poteto-mode/references/codex-tools.md | 6 +-- .../poteto-mode/references/model-catalog.md | 24 ++++++------ .../references/provider-dispatch.md | 10 +++-- .../poteto-mode/scripts/runner/cli.test.ts | 2 +- .../scripts/runner/registry.test.ts | 38 +++++++++++++++++-- .../scripts/runner/registry/index.ts | 2 +- .../scripts/runner/registry/render.ts | 4 +- .../poteto-mode/scripts/runner/run.test.ts | 19 ++++++++++ .../poteto-mode/scripts/runner/types.ts | 8 ++++ plugins/pstack/skills/setup-pstack/SKILL.md | 8 ++-- 15 files changed, 102 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 86a1415a..cf03d018 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 + +**Grok is a parent harness.** A Grok session resolves routes from the catalog's Grok parent column. A Grok lane whose model and effort already match the parent uses `spawn_subagent`. Every other Grok lane, and every Claude or Codex lane, uses the runner with `--parent grok`. The runner accepts a Grok provider from a Grok parent and sets `--permission-mode auto`. Claude-on-Claude and Codex-on-Codex stay on the native route. A headless Grok parent stays in auto mode, so a permission prompt reports the blocked call instead of cancelling the turn. + ## 1.20.0 gives Amp an opted-in writer Open Pstack 1.20.0 keeps the Cursor pstack 0.15.36 sync. Default roles and panels are unchanged. diff --git a/README.md b/README.md index db2cd593..b4ed3ca2 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Compared with Lauren's pstack, Open Pstack adds: - **A model and provider registry.** Models, efforts and providers live in one typed [registry](plugins/pstack/skills/poteto-mode/scripts/runner/registry/). The [model catalog](plugins/pstack/skills/poteto-mode/references/model-catalog.md) and the Claude-native agent files are generated from it, and the skills point at the catalog's role defaults instead of naming models. A new model is one registry entry. A new command-line provider is one adapter module and one line in the provider list. See the [design](docs/plans/2026-10-11-model-provider-registry.md). - **One skill tree for Claude Code and Codex.** Both apps read the same skills, and one [reference](plugins/pstack/skills/poteto-mode/references/codex-tools.md) maps how each app starts them. Lauren's pstack does not run in Codex. See [Claude Code and Codex](#claude-code-and-codex) below. -- **Models from other providers.** Claude Code and Codex hand work to other providers' models through each provider's own command-line tool, started by one [runner](plugins/pstack/skills/poteto-mode/references/provider-dispatch.md). The runner supports nine providers. Claude, Codex and Grok run lanes that read or write files. Devin, DeepSeek Harness, Antigravity, OpenCode, Amp and Cursor CLI run read-only lanes. The model catalog's [routes table](plugins/pstack/skills/poteto-mode/references/model-catalog.md#routes) lists how each one starts. Each run leaves a receipt with the exact model flags and, where the provider reports it, the model that answered. Every provider except Claude and Codex gets only an allowlisted environment. A Claude lane that writes files runs in Claude Code's Bash sandbox, with writes to the shared Git directory denied. A model that cannot run fails visibly; the runner never switches to a weaker model. +- **Models from other providers.** Claude Code and Codex hand work to other providers' models through each provider's own command-line tool, started by one [runner](plugins/pstack/skills/poteto-mode/references/provider-dispatch.md). The runner supports nine providers. Claude, Codex and Grok run lanes that read or write files. Devin, DeepSeek Harness, Antigravity, OpenCode, Amp and Cursor CLI run read-only lanes. The model catalog's [routes table](plugins/pstack/skills/poteto-mode/references/model-catalog.md#routes) lists how each one starts. A Grok parent runs a Grok lane whose model and effort already match the parent with `spawn_subagent`, and every other lane through the runner with `--parent grok`. Each run leaves a receipt with the exact model flags and, where the provider reports it, the model that answered. Every provider except Claude and Codex gets only an allowlisted environment. A Claude lane that writes files runs in Claude Code's Bash sandbox, with writes to the shared Git directory denied. A model that cannot run fails visibly; the runner never switches to a weaker model. - **Runs inside T3 Code.** In [T3 Code](plugins/pstack/skills/poteto-mode/references/provider-dispatch.md#t3-host), pstack keeps each role's model and effort. T3 starts each lane whose provider it offers, and the runner starts the rest. - **A checked model sheet.** [`setup-pstack`](plugins/pstack/skills/setup-pstack/SKILL.md) records each role as `provider:model@effort`, asks for one effort per model family, and probes each chosen model before it saves anything. A rerun that changes nothing keeps the sheet without asking again. - **Three-model panels.** `arena`, `architect` and `interrogate` default to Opus, Sol and Grok. Lauren's pstack defaults to Opus 5.5 and Grok; Open Pstack keeps three ([UPSTREAM.md](UPSTREAM.md)). diff --git a/UPSTREAM.md b/UPSTREAM.md index f611ffce..3036fd1f 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -71,6 +71,8 @@ The table above is the current Cursor sync point. Open Pstack 1.17.0 imported th - **#253 — T3 Code parent**: Cursor has no T3 Code route. The port adds the `T3 host` section to `provider-dispatch.md`: a lane whose provider T3 offers and has enabled goes through `delegate_task`, and any other lane takes the standalone route of the Claude Code or Codex runtime. `codex-tools.md` points to it, and `setup-pstack/SKILL.md` routes its probes and smoke panel the same way at its existing steps. No other skill body changes. Keep these lines during syncs. +- **#281 — Grok parent**: Cursor has no Grok parent. The port adds `grok` to `PARENTS` with `externalSelf`, so the generated route table has a Grok parent column and the runner accepts `--parent grok`, including a Grok provider. Claude and Codex stay native-only for their own provider. `provider-dispatch.md`, `codex-tools.md`, and `setup-pstack/SKILL.md` name `spawn_subagent` and that runner exception. Keep these lines during syncs. + ## Check for changes The repository already names Cursor's repository as the `cursor` remote in the maintainer checkout. A fresh clone can add it once: diff --git a/docs/reference.md b/docs/reference.md index 013242bd..46335f5a 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -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 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. A provider whose writes no sandbox confines, such as OpenCode, Cursor or Muse, writes only when the sheet holds ` writer sandbox: full-access`, which setup asks about when a writer role uses that provider. 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. In Grok, a Grok descriptor whose model and effort already match the parent uses `spawn_subagent`. Every other provider, and any other Grok descriptor, uses the runner with `--parent grok`. 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. A provider whose writes no sandbox confines, such as OpenCode, Cursor or Muse, writes only when the sheet holds ` writer sandbox: full-access`, which setup asks about when a writer role uses that provider. 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)). diff --git a/plugins/pstack/skills/poteto-mode/SKILL.md b/plugins/pstack/skills/poteto-mode/SKILL.md index 626cce23..b2ca6475 100644 --- a/plugins/pstack/skills/poteto-mode/SKILL.md +++ b/plugins/pstack/skills/poteto-mode/SKILL.md @@ -9,7 +9,7 @@ Bundled skills live at `..//SKILL.md` from this file, so put this file's a ## Platform Adaptation -These skills share one tree across Claude Code and Codex. Read [`references/provider-dispatch.md`](references/provider-dispatch.md) whenever a configured role launches. It defines the provider-qualified model descriptors, native/external route table, launcher, isolation, receipts, and dropout policy. Children never choose routes. When a skill names a Claude tool or built-in skill (`run`, `verify`, `plugin-dev:skill-development`), read [`references/codex-tools.md`](references/codex-tools.md) for the Codex equivalent. On Claude Code, read its [Waits on Claude Code](references/codex-tools.md#waits-on-claude-code) section before any wait. +These skills share one tree across Claude Code, Codex, and Grok. Read [`references/provider-dispatch.md`](references/provider-dispatch.md) whenever a configured role launches. It defines the provider-qualified model descriptors, native/external route table, launcher, isolation, receipts, and dropout policy. Children never choose routes. When a skill names a Claude tool or built-in skill (`run`, `verify`, `plugin-dev:skill-development`), read [`references/codex-tools.md`](references/codex-tools.md) for the Codex equivalent. On Claude Code, read its [Waits on Claude Code](references/codex-tools.md#waits-on-claude-code) section before any wait. ## Non-negotiables @@ -92,7 +92,7 @@ Read the leaf skill in full for any principle you apply. Each entry names when i ## Subagents -For `inherit-parent`, `auto`, or an unconfigured native ad-hoc helper, prefer `poteto-agent`. The chat that receives `/poteto-mode` runs the playbook. Never spawn a `poteto-agent` to run the playbook itself. A provider-qualified role instead follows provider dispatch: Claude's shipped frontier agent definitions select the model alias and requested effort, Codex passes both to `spawn_agent`, and external providers run through the deterministic launcher. Routed workflow skills set the task and access mode. Do not override their choices. +For `inherit-parent`, `auto`, or an unconfigured native ad-hoc helper, prefer `poteto-agent`. The chat that receives `/poteto-mode` runs the playbook. Never spawn a `poteto-agent` to run the playbook itself. A provider-qualified role instead follows provider dispatch: Claude's shipped frontier agent definitions select the model alias and requested effort, Codex passes both to `spawn_agent`, and Grok calls `spawn_subagent` when the descriptor's model and effort are already the parent's. Every other provider, and any other Grok descriptor, runs through the deterministic launcher. Routed workflow skills set the task and access mode. Do not override their choices. **Defaults for every delegation.** Start independent lanes together. In the main session, run a steer or resume of a running agent in the background too, because a foreground call holds your turn while other reports wait; a Claude Code subagent waits as [Waits on Claude Code](references/codex-tools.md#waits-on-claude-code) says. Use file pointers rather than inlined dumps, preserve only the tools or MCPs the task needs, and assign every writer a worktree or unique output directory. `/setup-pstack` configures the descriptor per role. A role without a sheet line uses its [role default](references/model-catalog.md#role-defaults); the panel roles default to one model from each of three providers. Each code playbook's configured model comes from its line (`feature, refactoring`, `bug-fix`, `perf-issue`, or `hillclimb`), and the hardest changes read `hardest tasks`. Prose and judgment read `judgment and prose`. `inherit-parent` and `auto` use the parent model natively and reduce provider diversity when used in a panel. diff --git a/plugins/pstack/skills/poteto-mode/references/codex-tools.md b/plugins/pstack/skills/poteto-mode/references/codex-tools.md index 9ce86a95..dbd2ceb2 100644 --- a/plugins/pstack/skills/poteto-mode/references/codex-tools.md +++ b/plugins/pstack/skills/poteto-mode/references/codex-tools.md @@ -60,7 +60,7 @@ The copy registers under its plain name beside the plugin's `pstack:`, an | Search the web | `web_search` | | Invoke a skill (the `Skill` tool, `/command`) | Skills load natively. Follow the instructions presented. | | `paths` frontmatter scopes automatic loading | Claude Code only. On Codex, invoke `pstack:typescript-best-practices` by name. | -| Dispatch a subagent (the `Agent`/`Task` tool) | `spawn_agent` | +| Dispatch a subagent (the `Agent`/`Task` tool) | `spawn_agent`. On Grok this is `spawn_subagent`. | | Dispatch N parallel subagents in one turn | N `spawn_agent` calls in one response | | Wait for a subagent result | `wait_agent` | | Free a finished subagent slot | `close_agent` | @@ -99,7 +99,7 @@ poteto-mode's Subagents section sets the Claude-specific default `subagent_type: ## Models and providers -Do not replace every configured entry with a Codex model. `/setup-pstack` writes portable descriptors, such as the [role defaults](model-catalog.md#role-defaults). 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 runs one model from each of three providers 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 the [role defaults](model-catalog.md#role-defaults). In a Codex parent, only `codex:*` is native. Route Claude and Grok descriptors through the external launcher exactly as `provider-dispatch.md` specifies. In a Grok parent, `inherit-parent`, `auto`, and a Grok descriptor that already matches the parent's model and effort use `spawn_subagent`. Every other provider, and any other Grok descriptor, uses the runner with `--parent grok`. The runner still rejects a Claude provider from a Claude parent and a Codex provider from a Codex parent. It accepts `--parent grok` with a Grok provider. The default panel runs one model from each of three providers and contains no older GPT or Claude substitute. ## Claude built-in skills pstack references @@ -114,7 +114,7 @@ Some triggers name skills that ship with Claude Code, not pstack. They do not ex ## Vendored scripts -`skills/poteto-mode/scripts/` ships the `watch-pr` PR watcher, `worktree-audit.sh`, and `runner/pstack-runner`. They are plain bun and bash, so they run the same on Codex; invoke them through `shell`. The external runner additionally needs the assigned `claude`, `codex`, or `grok` executable already authenticated. It rejects a Codex provider when Codex is the parent because that lane belongs on native `spawn_agent`. The other scripts need `bun`, `gh` (or Origin's `origin` CLI where the playbook resolves it), and (for `worktree-audit.sh`) `jq` and `rg`. `worktree-audit.sh` reads Claude Code transcripts under `~/.claude/projects/`; point it at your runtime's transcript directory instead when you run it elsewhere. +`skills/poteto-mode/scripts/` ships the `watch-pr` PR watcher, `worktree-audit.sh`, and `runner/pstack-runner`. They are plain bun and bash, so they run the same on Codex; invoke them through `shell`. The external runner additionally needs the assigned `claude`, `codex`, or `grok` executable already authenticated. It rejects a Codex provider when Codex is the parent because that lane belongs on native `spawn_agent`. It rejects a Claude provider when Claude Code is the parent for the same reason. A Grok parent may run a Grok provider through the runner when `spawn_subagent` cannot carry the model or the effort. The other scripts need `bun`, `gh` (or Origin's `origin` CLI where the playbook resolves it), and (for `worktree-audit.sh`) `jq` and `rg`. `worktree-audit.sh` reads Claude Code transcripts under `~/.claude/projects/`; point it at your runtime's transcript directory instead when you run it elsewhere. ## Instructions file diff --git a/plugins/pstack/skills/poteto-mode/references/model-catalog.md b/plugins/pstack/skills/poteto-mode/references/model-catalog.md index e1976c62..fb765cad 100644 --- a/plugins/pstack/skills/poteto-mode/references/model-catalog.md +++ b/plugins/pstack/skills/poteto-mode/references/model-catalog.md @@ -63,18 +63,18 @@ Rolling aliases: ## Routes -| Provider | Claude Code parent | Codex parent | Access modes | Prompt | Model proof | Child environment | Host prerequisites | -| --- | --- | --- | --- | --- | --- | --- | --- | -| claude | native `Agent` | external runner | `read-only`, `isolated-write` | stdin | provider report | read-only: inherited; isolated-write: inherited without `GIT_*` | - | -| codex | external runner | native `spawn_agent` | `read-only`, `isolated-write`, `isolated-write-full-access` | stdin | pinned argv | inherited | - | -| grok | external runner | external runner | `read-only`, `isolated-write` | file | provider report | allowlist | Linux: Landlock and `bwrap`; macOS: any `/var/run/docker.sock` symlink points inside Grok's runtime-socket list | -| devin | external runner | external runner | `read-only`, `isolated-write`, `isolated-write-full-access` | file | provider report | allowlist | - | -| opencode | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | -| dsh | external runner | external runner | `read-only`, `isolated-write` | stdin | pinned argv | allowlist | dsh 0.2.1-rc.2 exactly | -| amp | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | -| agy | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | -| cursor | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | -| muse | external runner | external runner | `read-only`, `isolated-write-full-access` | file | pinned argv | allowlist | - | +| Provider | Claude Code parent | Codex parent | Grok parent | Access modes | Prompt | Model proof | Child environment | Host prerequisites | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| claude | native `Agent` | external runner | external runner | `read-only`, `isolated-write` | stdin | provider report | read-only: inherited; isolated-write: inherited without `GIT_*` | - | +| codex | external runner | native `spawn_agent` | external runner | `read-only`, `isolated-write`, `isolated-write-full-access` | stdin | pinned argv | inherited | - | +| grok | external runner | external runner | native `spawn_subagent` or external runner | `read-only`, `isolated-write` | file | provider report | allowlist | Linux: Landlock and `bwrap`; macOS: any `/var/run/docker.sock` symlink points inside Grok's runtime-socket list | +| devin | external runner | external runner | external runner | `read-only`, `isolated-write`, `isolated-write-full-access` | file | provider report | allowlist | - | +| opencode | external runner | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | +| dsh | external runner | external runner | external runner | `read-only`, `isolated-write` | stdin | pinned argv | allowlist | dsh 0.2.1-rc.2 exactly | +| amp | external runner | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | +| agy | external runner | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | +| cursor | external runner | external runner | external runner | `read-only`, `isolated-write-full-access` | stdin | pinned argv | allowlist | - | +| muse | external runner | external runner | external runner | `read-only`, `isolated-write-full-access` | file | pinned argv | allowlist | - | The runner refuses an access mode outside a provider's Access modes cell before launch. `isolated-write-full-access` is a writer capability, not a `--mode` value: the runner takes it as `--mode isolated-write --writer-sandbox full-access`, which a parent passes only when the model sheet opts in, as [`provider-dispatch.md`](provider-dispatch.md#writer-sandbox) says. A provider whose cell lists `isolated-write-full-access` without `isolated-write` has no confined writer and writes only with that opt-in. diff --git a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md index 0b05aa9e..d2081f2d 100644 --- a/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md +++ b/plugins/pstack/skills/poteto-mode/references/provider-dispatch.md @@ -14,7 +14,7 @@ The allowed effort universe is exactly `low`, `medium`, `high`, `xhigh`, `max`, A rolling alias, such as `opus`, names a family whose CLI resolves it 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 revision of the matching family, as the catalog's rolling-alias table gives it. -A descriptor whose `(provider, model)` matches no matrix row, kept predecessor, or rolling-alias revision is an unregistered pair. It runs unchanged on an external route and on Codex's native route. A Claude-native route needs a generated agent, so an unregistered Claude model on a Claude Code parent is a dropout. Never guess a replacement. +A descriptor whose `(provider, model)` matches no matrix row, kept predecessor, or rolling-alias revision is an unregistered pair. It runs unchanged on an external route and on Codex's native route. A Claude-native route needs a generated agent, so an unregistered Claude model on a Claude Code parent is a dropout. An unregistered Grok model on a Grok parent uses the runner, which keeps that model. Do not replace it with the parent model. Never guess a replacement. ## Read-time normalization @@ -28,7 +28,7 @@ A kept predecessor in the catalog matches its family. Dispatch it with the sheet The top-level harness resolves the route once. A child receives an assigned provider, model, effort, access mode, prompt, working directory, and output path. A child never detects the harness, chooses a provider, or launches another model. Environment markers may corroborate the top-level harness before fan-out, but nested processes inherit parent markers and must not use them for routing. -The catalog's [route table](model-catalog.md#routes) gives each provider's route from each parent. Outside [T3](#t3-host), a provider native to the parent uses the parent's subagent primitive. Every other provider uses the external runner. +The catalog's [route table](model-catalog.md#routes) gives each provider's route from each parent. Outside [T3](#t3-host), a Claude or Codex provider on its own parent uses that parent's subagent primitive, and every other provider from those parents uses the external runner. A Grok parent uses `spawn_subagent` for `inherit-parent`, `auto`, and a Grok descriptor whose model and effort are already the parent's. It uses the external runner for every other descriptor, including any other Grok descriptor. `inherit-parent` and `auto` remain aliases. Outside [T3](#t3-host), they use the parent's current model and effort through its native subagent primitive. In a panel they still consume one lane, but they reduce provider diversity; say so in the synthesis record. @@ -78,8 +78,9 @@ 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--` using that row's family and the descriptor's effort. Those definitions select the row's model and requested effort. `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. Dispatch and wait as [Waits on Claude Code](codex-tools.md#waits-on-claude-code) says. - 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. +- Grok: call `spawn_subagent` with the complete task, grounding paths, access mode, and unique output location in the prompt. Pass `model` when the tool lists it. The tool takes no effort, so use this route only when the descriptor's model and effort are already the parent's. Use `isolation` `worktree` for a writer and `none` for a read-only lane. The call runs in the background. Keep its id and read the result with `get_command_or_subagent_output`. A native child inherits the parent session's permission mode. A headless Grok parent uses auto mode, because headless Grok cancels the turn on a permission prompt and auto mode reports the blocked call to the model instead. -Do not send a same-provider descriptor to the external runner. It rejects that call because the native route is cheaper and already available. +Do not send a Claude or Codex descriptor to the external runner from that same parent. The runner rejects that call because the native route is available. A Grok parent may send a Grok descriptor to the runner. Use native `spawn_subagent` for `inherit-parent`, `auto`, and a Grok descriptor whose model and effort are already the parent's. Send every other Grok descriptor through the runner with `--parent grok`. That launch sets `--permission-mode auto`, the model, and the effort. Do not switch to the runner after a native lane fails. ## External lanes @@ -87,7 +88,7 @@ The launcher lives at `skills/poteto-mode/scripts/runner/pstack-runner` under th ```text pstack-runner \ - --parent \ + --parent \ --provider \ --model \ --effort \ @@ -122,6 +123,7 @@ The parent invocation must itself be resumable background work: - Claude Code: call the launcher through a Bash tool invocation with `run_in_background: true` and retain its task ID. A foreground Bash tool call has an automatic ten-minute ceiling even when the runner's own timeout is longer. Shelling out with `&` and losing the task handle is not equivalent. A subagent that must wait on the launcher follows [Waits on Claude Code](codex-tools.md#waits-on-claude-code). - Codex: run the launcher in a persistent exec session that returns a session ID, then wait or poll that handle. Do not hold one foreground tool call open for the model's full runtime. +- Grok: start the launcher as background work that returns a task id, then read that id with `get_command_or_subagent_output`. Do not hold one foreground tool call open for the model's full runtime. Start the background process, continue launching the other lanes, then drain their handles. Native and external lanes belong in the same fan-out phase. diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts index 6a8ab6e9..6a87152f 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts @@ -79,7 +79,7 @@ describe("runner CLI parsing", () => { let stdout = ""; expect(await main(["--help"], Date.now(), { stdout: (value) => { stdout += value; }, stderr: () => {} })).toBe(0); expect(stdout).toStartWith( - "Usage: pstack-runner --parent --provider \\\n" + "Usage: pstack-runner --parent --provider \\\n" ); }); diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/registry.test.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/registry.test.ts index 3e5c749b..75f83f73 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/registry.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/registry.test.ts @@ -22,7 +22,7 @@ import { writeGenerated, } from "./registry/render.ts"; import { type Access, access } from "./registry/contract.ts"; -import { EFFORTS } from "./types.ts"; +import { EFFORTS, PARENTS, type Parent } from "./types.ts"; const RUNNER = import.meta.dir; const PLUGIN_ROOT = join(RUNNER, "../../../.."); @@ -148,10 +148,12 @@ describe("registry data", () => { const routes = renderCatalog(REGISTRY).content.split("\n## Routes\n")[1]!.split("\n## ")[0]!; for (const adapter of REGISTRY.adapters) { const cells = routes.split("\n").find((line) => line.startsWith(`| ${adapter.id} |`))!.split(" | "); - expect(cells.slice(1, 3)).toEqual([ + expect(cells.slice(1, 4)).toEqual([ adapter.id === "claude" ? "native `Agent`" : "external runner", adapter.id === "codex" ? "native `spawn_agent`" : "external runner", + adapter.id === "grok" ? "native `spawn_subagent` or external runner" : "external runner", ]); + expect(routes).toContain("| Provider | Claude Code parent | Codex parent | Grok parent |"); } }); }); @@ -229,6 +231,9 @@ describe("hand-written dispatch and setup rules", () => { const native = section(dispatch, "## Native lanes", "## External lanes"); expect(native).toContain("match the descriptor's `(provider, model)` to one model-matrix row"); expect(native).toContain("`pstack--`"); + expect(native).toContain("`spawn_subagent`"); + expect(native).toContain("A headless Grok parent uses auto mode"); + expect(dispatch).toContain("--parent "); }); it("normalizes rolling-alias pins and keeps predecessors before any runtime route", () => { @@ -547,10 +552,37 @@ describe("resolveLane", () => { expect(() => resolveLane(request({ parent: "claude" }))) .toThrow("provider claude is native to parent claude; use the parent subagent primitive"); expect(() => resolveLane(request({ provider: "kiro" }))).toThrow("provider must be one of: claude, codex, grok, devin, opencode, dsh, amp, agy, cursor, muse"); - expect(() => resolveLane(request({ parent: "grok" }))).toThrow("parent must be one of: claude, codex"); + expect(() => resolveLane(request({ parent: "kiro" }))).toThrow("parent must be one of: claude, codex, grok"); expect(() => resolveLane(request({ mode: "full" }))).toThrow("mode must be one of: read-only, isolated-write"); expect(() => resolveLane(request({ model: " " }))).toThrow("model is required"); }); + + it("accepts a grok parent for every provider, including grok, and keeps auto mode on that lane", () => { + const parents = Object.keys(PARENTS) as Parent[]; + for (const parent of parents) { + for (const adapter of REGISTRY.adapters) { + const family = REGISTRY.families.find((entry) => entry.provider === adapter.id); + const laneRequest = request({ + parent, + provider: adapter.id, + model: family?.model ?? "any", + effort: family?.defaultEffort ?? "default", + }); + const sameProvider = adapter.id === parent; + if (sameProvider && !PARENTS[parent].externalSelf) { + expect(() => resolveLane(laneRequest)).toThrow("use the parent subagent primitive"); + continue; + } + const lane = resolveLane(laneRequest); + expect(lane.parent).toBe(parent); + expect(lane.adapter.id).toBe(adapter.id); + if (parent === "grok" && adapter.id === "grok") { + expect(lane.adapter.launch.argv(lane, { sharedGitDir: null, providerSidecar: null })) + .toEqual(expect.arrayContaining(["--permission-mode", "auto"])); + } + } + } + }); }); describe("module boundaries", () => { diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/registry/index.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/registry/index.ts index 25973961..4ad82f4e 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/registry/index.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/registry/index.ts @@ -71,7 +71,7 @@ export function resolveLane(request: LaneRequest): Lane { } if (request.model.trim().length === 0) throw new UsageError("model is required"); const effort = oneOf("effort", request.effort, EFFORTS); - if (adapter.id === parent) { + if (adapter.id === parent && !PARENTS[parent].externalSelf) { throw new UsageError( `provider ${adapter.id} is native to parent ${parent}; use the parent subagent primitive` ); diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/registry/render.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/registry/render.ts index 957ddbd3..c91ec87e 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/registry/render.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/registry/render.ts @@ -30,7 +30,9 @@ const table = (header: readonly string[], rows: readonly (readonly string[])[]): ]; function route(adapter: ProviderAdapter, parent: Parent): string { - return adapter.id === parent ? `native ${code(PARENTS[parent].native)}` : "external runner"; + if (adapter.id !== parent) return "external runner"; + const native = `native ${code(PARENTS[parent].native)}`; + return PARENTS[parent].externalSelf ? `${native} or external runner` : native; } function policy(value: EnvPolicy, long: boolean): string { diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/run.test.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/run.test.ts index 20e7095b..0cf0b7a1 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/run.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/run.test.ts @@ -1969,6 +1969,25 @@ describe("childEnvironment", () => { CODEX_CI: "1", KEEP_ME: "yes", }); + const grokParent = { + ...source, + GROK_SESSION_ID: "sess", + GROK_AGENT: "agent", + GROK_HOME: "/grok", + }; + expect(childEnvironment(laneOf({ ...options("claude"), parent: "grok", mode: "read-only" }), grokParent)).toEqual({ + PATH: "/bin", + CODEX_THREAD_ID: "codex", + CODEX_CI: "1", + CLAUDECODE: "1", + CLAUDE_CODE_CHILD_SESSION: "1", + KEEP_ME: "yes", + GROK_HOME: "/grok", + }); + expect(childEnvironment(laneOf({ ...options("grok"), parent: "grok", mode: "read-only" }), grokParent)).toEqual({ + PATH: "/bin", + GROK_HOME: "/grok", + }); }); it("passes a Grok lane only allowlisted variables in both modes", () => { diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/types.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/types.ts index 902e13dc..e525a691 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/types.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/types.ts @@ -2,6 +2,7 @@ export const PARENTS = { claude: { harness: "Claude Code", native: "Agent", + externalSelf: false, identity: [ "CLAUDECODE", "CLAUDE_CODE_CHILD_SESSION", @@ -12,6 +13,7 @@ export const PARENTS = { codex: { harness: "Codex", native: "spawn_agent", + externalSelf: false, identity: [ "CODEX_THREAD_ID", "CODEX_SESSION_ID", @@ -22,6 +24,12 @@ export const PARENTS = { "CODEX_INTERNAL_ORIGINATOR_OVERRIDE", ], }, + grok: { + harness: "Grok", + native: "spawn_subagent", + externalSelf: true, + identity: ["GROK_SESSION_ID", "GROK_AGENT"], + }, } as const; export const EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra", "default"] as const; export const ACCESS_MODES = ["read-only", "isolated-write"] as const; diff --git a/plugins/pstack/skills/setup-pstack/SKILL.md b/plugins/pstack/skills/setup-pstack/SKILL.md index 8bf84e39..b23ff838 100644 --- a/plugins/pstack/skills/setup-pstack/SKILL.md +++ b/plugins/pstack/skills/setup-pstack/SKILL.md @@ -29,7 +29,7 @@ Codex writes `/pstack-models.md`. Codex has no `@` include, so mirr ### 1. Establish the parent -Use the harness and tool surface running this skill: Claude Code, Codex, or T3 Code running one of them. Environment markers may corroborate that top-level answer, but do not launch a child and ask it to detect where it came from. Record the parent because the same descriptor takes a different route in each harness. On T3, record the orchestration harness and the model runtime as [T3 host](../poteto-mode/references/provider-dispatch.md#t3-host) says. The runtime, Claude Code or Codex, selects ``, the sheet, and the integration below. +Use the harness and tool surface running this skill: Claude Code, Codex, T3 Code running one of them, or Grok. Environment markers may corroborate that top-level answer, but do not launch a child and ask it to detect where it came from. Record the parent because the same descriptor takes a different route in each harness. On T3, record the orchestration harness and the model runtime as [T3 host](../poteto-mode/references/provider-dispatch.md#t3-host) says. The runtime, Claude Code or Codex, selects ``, the sheet, and the integration below. Grok is a parent for dispatch. This skill still writes the sheet only for Claude Code and Codex. On a Grok parent, probe in step 5, then stop before step 6 and do not write a config home. ### 2. Load current state @@ -74,11 +74,11 @@ Probe only the selected `provider:model@effort` pair of each assigned family. Ru If a Grok probe fails because the host cannot enforce its bounded sandbox, see [Host and parent prerequisites](../poteto-mode/references/provider-dispatch.md#host-and-parent-prerequisites); keep the active configuration unchanged. -Find the family's provider in the [route table](../poteto-mode/references/model-catalog.md#routes). A native route gets a native one-turn probe: on Claude Code, the family's `pstack--` agent; on Codex, `spawn_agent` with the descriptor's model and `reasoning_effort`. An external route gets one `pstack-runner` call with a tiny read-only probe prompt. The runner's preflight checks the CLI, the login and the model, and its receipt is the result. A T3 route gets one `delegate_task` probe, and its T3 result record must meet T3 host's completion rules. +Find the family's provider in the [route table](../poteto-mode/references/model-catalog.md#routes). A native-only route gets a native one-turn probe: on Claude Code, the family's `pstack--` agent; on Codex, `spawn_agent` with the descriptor's model and `reasoning_effort`. On Grok, `inherit-parent`, `auto`, and a Grok descriptor whose model and effort are already the parent's use `spawn_subagent`. An external route gets one `pstack-runner` call with a tiny read-only probe prompt and `--parent` set to the parent running this skill. On a Grok parent that call also covers every Grok descriptor `spawn_subagent` cannot carry. The runner's preflight checks the CLI, the login and the model, and its receipt is the result. A T3 route gets one `delegate_task` probe, and its T3 result record must meet T3 host's completion rules. -Use a tiny read-only probe that returns a unique marker. For each family that an external writer role uses on a provider whose writer needs a `full-access` setting line, also run one `isolated-write` probe in a disposable linked worktree, with `--writer-sandbox full-access`, that creates a small program and runs it; it passes only when the program's output file holds the expected marker. A login-status command alone proves credentials, not that the requested model and effort flags run. Record native, external, and T3 results separately. Never call the external launcher for the parent's own provider. +Use a tiny read-only probe that returns a unique marker. For each family that an external writer role uses on a provider whose writer needs a `full-access` setting line, also run one `isolated-write` probe in a disposable linked worktree, with `--writer-sandbox full-access`, that creates a small program and runs it; it passes only when the program's output file holds the expected marker. A login-status command alone proves credentials, not that the requested model and effort flags run. Record native, external, and T3 results separately. Never call the external launcher for a Claude or Codex parent's own provider. A Grok parent may call it for a Grok descriptor when `spawn_subagent` cannot carry the model or the effort. -Receipts and native transcripts prove the requested effort and the route. T3 result records prove only T3's launch configuration. They do not prove a provider's hidden applied reasoning depth. There is no implicit timeout, weaker-model fallback, same-provider external fallback, or second mutable configuration source. +Receipts and native transcripts prove the requested effort and the route. T3 result records prove only T3's launch configuration. They do not prove a provider's hidden applied reasoning depth. There is no implicit timeout, weaker-model fallback, or second mutable configuration source. A failed native lane is not retried through the runner. ### 6. Render, preserving role families From a4c0a70b04c0ee416a1d67970ea8f3811994167d Mon Sep 17 00:00:00 2001 From: max-metaplanet Date: Fri, 6 Nov 2026 13:30:50 -0800 Subject: [PATCH 2/3] feat/no-ticket(runner): correct same-provider help for a grok parent The runner help and reference said every same-provider call is rejected. A Grok parent may run a Grok provider, and the help now says so. --- CHANGES.md | 2 +- docs/reference.md | 4 ++-- .../pstack/skills/poteto-mode/scripts/runner/cli.test.ts | 2 ++ plugins/pstack/skills/poteto-mode/scripts/runner/cli.ts | 6 ++++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cf03d018..c336014e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ This port applies the Cursor → Claude Code substitutions in skill bodies. Earl ## Unreleased -**Grok is a parent harness.** A Grok session resolves routes from the catalog's Grok parent column. A Grok lane whose model and effort already match the parent uses `spawn_subagent`. Every other Grok lane, and every Claude or Codex lane, uses the runner with `--parent grok`. The runner accepts a Grok provider from a Grok parent and sets `--permission-mode auto`. Claude-on-Claude and Codex-on-Codex stay on the native route. A headless Grok parent stays in auto mode, so a permission prompt reports the blocked call instead of cancelling the turn. +**Grok is a parent harness.** A Grok session resolves routes from the catalog's Grok parent column. A Grok lane whose model and effort already match the parent uses `spawn_subagent`. Every other Grok lane, and every Claude or Codex lane, uses the runner with `--parent grok`. The runner accepts a Grok provider from a Grok parent and sets `--permission-mode auto`. Its help says a Claude or Codex parent still rejects its own provider. Claude-on-Claude and Codex-on-Codex stay on the native route. A headless Grok parent stays in auto mode, so a permission prompt reports the blocked call instead of cancelling the turn. ## 1.20.0 gives Amp an opted-in writer diff --git a/docs/reference.md b/docs/reference.md index 46335f5a..7c269a90 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -113,7 +113,7 @@ Not declared as deps, but referenced in skill bodies: - **Bash on Windows** — the SessionStart hook runs through `hooks/run-hook.cmd`, which looks for Git for Windows' `bash.exe` in its standard system-wide and per-user locations, then for a `bash` on `PATH` (MSYS2, Cygwin). Without one, the hook exits 0 and skips the pstack startup context, as the superpowers wrapper it comes from does. Install [Git for Windows](https://git-scm.com/download/win) to get the context. In Codex, the hook starts `C:\Windows\System32\cmd.exe` by full path, because one command must work in both PowerShell and cmd. A Windows installed outside `C:\Windows` does not run the hook. - **macOS, Linux or WSL2 for Claude writer lanes** — Claude Code's sandbox does not run on native Windows, so a Claude writer lane needs one of these hosts. - **`node`** — runs `skills/poteto-mode/scripts/check-plan.mjs`. The checker uses only Node built-ins and does not need Bun. -- **Provider CLIs** — the external runner uses the assigned subscribed CLI directly, one per provider in the [model catalog's route table](../plugins/pstack/skills/poteto-mode/references/model-catalog.md#routes). Install and authenticate only the providers present in your model sheet. Some providers run read-only lanes only. The catalog notes for OpenCode, Amp, Cursor, Devin, agy and dsh say what repository and host configuration their CLIs load into a lane. Same-provider work stays native; the runner refuses it. +- **Provider CLIs** — the external runner uses the assigned subscribed CLI directly, one per provider in the [model catalog's route table](../plugins/pstack/skills/poteto-mode/references/model-catalog.md#routes). Install and authenticate only the providers present in your model sheet. Some providers run read-only lanes only. The catalog notes for OpenCode, Amp, Cursor, Devin, agy and dsh say what repository and host configuration their CLIs load into a lane. A Claude or Codex provider on its own parent stays native, and the runner refuses that call. A Grok parent may run a Grok provider through the runner. - **`jq` and `rg` (ripgrep)** — only for `scripts/worktree-audit.sh` (the Worktree cleanup playbook). Without them the audit still runs but blanks its PR and LAST_CHAT columns, so it warns on stderr rather than returning a table that looks complete. No third-party plugins. The harsher-critique escape hatch lives in the bundled `thermo-nuclear-code-quality-review` skill (imported from cursor-team-kit), not in an external plugin. @@ -206,7 +206,7 @@ The port is editorial, not mechanical. Anywhere upstream pstack assumed Cursor-s | Cursor cloud agents (`environment: "cloud"`, `cloud_base_branch`) | Local background subagents (`run_in_background: true`), isolated by git worktree | | Orchestrate's Project tracker (`notes.md`) and Project store | `/orchestrate//notes.md`, which survives the session restarts a multi-day program expects | | Model rule `~/.cursor/rules/pstack-models.mdc` | Override sheet `~/.claude/pstack-models.md`, included from `CLAUDE.md` | -| Multi-model panels (arena, architect, interrogate) | Provider dispatch runs Open Pstack's default three-model panel of Opus, Sol, and Grok from the model catalog's [role defaults](../plugins/pstack/skills/poteto-mode/references/model-catalog.md#role-defaults). Same-provider lanes stay native; external lanes use the bundled runner. Lauren's pstack now defaults to Opus 5.5 and Grok. | +| Multi-model panels (arena, architect, interrogate) | Provider dispatch runs Open Pstack's default three-model panel of Opus, Sol, and Grok from the model catalog's [role defaults](../plugins/pstack/skills/poteto-mode/references/model-catalog.md#role-defaults). A Claude or Codex lane on its own parent stays native. A Grok parent may run a Grok lane through the runner. Every other lane uses the bundled runner. Lauren's pstack now defaults to Opus 5.5 and Grok. | ### Cross-vendor dispatch diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts index 6a87152f..7d30fc93 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts @@ -81,6 +81,8 @@ describe("runner CLI parsing", () => { expect(stdout).toStartWith( "Usage: pstack-runner --parent --provider \\\n" ); + expect(stdout).toContain("A Grok parent may run a Grok provider here"); + expect(stdout).toContain("A Claude or Codex parent rejects its own"); }); it("refuses an unnormalized Claude revision with a usage error", async () => { diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.ts index ed599fe1..f39a8a67 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.ts @@ -9,8 +9,10 @@ const HELP = `Usage: pstack-runner --parent <${Object.keys(PARENTS).join("|")}> [--writer-sandbox ] \\ --prompt --cwd --output --receipt [--timeout ] -Runs exactly one external model lane. Same-provider calls are rejected; use the -parent harness's native subagent primitive for those lanes. Output and receipt +Runs exactly one external model lane. A Claude or Codex parent rejects its own +provider; use that parent's native subagent primitive. A Grok parent may run a +Grok provider here when spawn_subagent cannot carry the model or the effort. +Output and receipt paths must not already exist. There is no implicit timeout. Pass --timeout only when the user or task supplies a real deadline; it is one end-to-end launcher deadline shared by setup, preflight, and model execution. --writer-sandbox From e6ecc3c73d98fba267e96d62a64a2a651f3c2a72 Mon Sep 17 00:00:00 2001 From: max-metaplanet Date: Fri, 6 Nov 2026 13:31:02 -0800 Subject: [PATCH 3/3] feat/no-ticket(runner): match grok parent help across wrapped lines The help assertion now ignores the line wrap, so it checks the sentence the runner prints. --- plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts index 7d30fc93..259ece1e 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/runner/cli.test.ts @@ -81,8 +81,9 @@ describe("runner CLI parsing", () => { expect(stdout).toStartWith( "Usage: pstack-runner --parent --provider \\\n" ); - expect(stdout).toContain("A Grok parent may run a Grok provider here"); - expect(stdout).toContain("A Claude or Codex parent rejects its own"); + const help = stdout.replaceAll("\n", " "); + expect(help).toContain("A Grok parent may run a Grok provider here"); + expect(help).toContain("A Claude or Codex parent rejects its own provider"); }); it("refuses an unnormalized Claude revision with a usage error", async () => {