From b93e8117880f695bc49ef7b1ef72c09bfcc7c1de Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 09:17:54 -0700 Subject: [PATCH 01/13] fix(pstack): let poteto-agent find poteto-mode, drop ignored frontmatter, document the / menu (#597) * fix(pstack): let poteto-agent find poteto-mode without a path Co-authored-by: lauren * chore(poteto-mode): drop the mode and reminder frontmatter keys Cursor ignores both keys, so the reminder text never reached the model. Co-authored-by: lauren * docs(pstack): tell users to pick /poteto-mode from the slash menu Co-authored-by: lauren * chore(pstack): bump version to 0.15.23 Co-authored-by: lauren * fix(poteto-agent): pick the highest plugin.json version when several poteto-mode copies match Co-authored-by: lauren * chore(pstack): drop version bump --------- Co-authored-by: Cursor Agent --- pstack/README.md | 2 ++ pstack/agents/poteto-agent.md | 2 +- pstack/skills/poteto-mode/SKILL.md | 2 -- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pstack/README.md b/pstack/README.md index 5e34dbff9..5a6a39d70 100644 --- a/pstack/README.md +++ b/pstack/README.md @@ -33,6 +33,8 @@ that's it. the other skills are situational; the mode skill uses them for you as use [`/poteto-mode`](./skills/poteto-mode/SKILL.md) at the start of a task. it reads your request, picks from a set of playbooks, and runs the other skills as the steps need them. +pick `/poteto-mode` from the `/` menu before you type your task, so cursor attaches the skill to the message. + ### just use [`/poteto-mode`](./skills/poteto-mode/SKILL.md) this skill is the main shortcut. i use it whenever i need the agent to do rigorous engineering work. it comes with twenty-three playbooks: diff --git a/pstack/agents/poteto-agent.md b/pstack/agents/poteto-agent.md index c5515f513..e1d2df2e8 100644 --- a/pstack/agents/poteto-agent.md +++ b/pstack/agents/poteto-agent.md @@ -6,4 +6,4 @@ is_background: true # Poteto subagent -You are operating as poteto-mode's full agent style. The chat that receives `/poteto-mode` owns the playbook, the todo list, and every Task spawn. You are one of its delegates. Read the `poteto-mode` skill's `SKILL.md` in full before doing any work, including its inline Principles index. Use the absolute path your parent gave you. Navigate to a leaf `principle-*` skill whenever you apply that principle. +You are operating as poteto-mode's full agent style. The chat that receives `/poteto-mode` owns the playbook, the todo list, and every Task spawn. You are one of its delegates. Read the `poteto-mode` skill's `SKILL.md` in full before doing any work, including its inline Principles index. Use the absolute path your parent gave you. With no path, before any other work, search `~/.cursor/plugins/` for a file whose path ends in `skills/poteto-mode/SKILL.md`. If more than one file matches, use the copy whose `.cursor-plugin/plugin.json` has the highest `version`. Your skill catalog hides this skill, so a missing catalog entry does not mean pstack is absent. If no such file exists, stop and tell your parent. Navigate to a leaf `principle-*` skill whenever you apply that principle. diff --git a/pstack/skills/poteto-mode/SKILL.md b/pstack/skills/poteto-mode/SKILL.md index d5be8a10b..fef2b3689 100644 --- a/pstack/skills/poteto-mode/SKILL.md +++ b/pstack/skills/poteto-mode/SKILL.md @@ -2,10 +2,8 @@ name: Poteto Mode description: poteto's agent style for concise, detailed responses, deliberate subagents, unslopped prose, simple code, and verified work. Use for poteto, /poteto-mode, or requests to work in this style. disable-model-invocation: true -mode: true icon: crown color: yellow -reminder: New task? Playbook match or rigor needed -> apply /poteto-mode. Casual turn or user opts out -> don't. --- # Poteto mode From 7d8d9091e59a1fe9600bb317a1b7f69a31daa349 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 09:18:15 -0700 Subject: [PATCH 02/13] feat(pstack): add /in-your-own-words to play back the goal before work (#602) * feat(pstack): add /in-your-own-words to play back the goal before work * chore(pstack): bump to 0.15.24 * refactor(pstack): tighten /in-your-own-words * refactor(pstack): cut /in-your-own-words to the core * refactor(pstack): reduce /in-your-own-words to one line * chore(pstack): drop version bump --------- Co-authored-by: Cursor Agent --- pstack/README.md | 1 + pstack/skills/in-your-own-words/SKILL.md | 7 +++++++ pstack/skills/poteto-help/SKILL.md | 1 + pstack/skills/poteto-help/references/recipes.md | 1 + 4 files changed, 10 insertions(+) create mode 100644 pstack/skills/in-your-own-words/SKILL.md diff --git a/pstack/README.md b/pstack/README.md index 5a6a39d70..407fe2e8b 100644 --- a/pstack/README.md +++ b/pstack/README.md @@ -113,6 +113,7 @@ to keep [`/poteto-mode`](./skills/poteto-mode/SKILL.md) on across turns, pick it |---|---| | [`/poteto-mode`](./skills/poteto-mode/SKILL.md) | default entry point for any non-trivial task. | | [`/poteto-help`](./skills/poteto-help/SKILL.md) | you're new to pstack, or unsure which skill, playbook, or principle fits. finds out what you're trying to do, answers that part, and hands you a prompt to type. runs only when you type `/poteto-help`. | +| [`/in-your-own-words`](./skills/in-your-own-words/SKILL.md) | you want the agent to play back your goal and the problem before it starts. runs only when you type `/in-your-own-words`. | | [`/how`](./skills/how/SKILL.md) | you want a walkthrough of how a subsystem works. | | [`/why`](./skills/why/SKILL.md) | you want to know why something was built this way. discovers available MCPs at run time and queries each evidence category in parallel (source control, issue tracker, long-form docs, real-time chat, infra observability, error tracking, analytics warehouse). | | [`/recall`](./skills/recall/SKILL.md) | you're starting or resuming work and want your recent context on a topic rebuilt from your own chat history and the shared record, handed back as a tight current-state brief. | diff --git a/pstack/skills/in-your-own-words/SKILL.md b/pstack/skills/in-your-own-words/SKILL.md new file mode 100644 index 000000000..f30d03e29 --- /dev/null +++ b/pstack/skills/in-your-own-words/SKILL.md @@ -0,0 +1,7 @@ +--- +name: in-your-own-words +description: "Restate the user's goal and intent in your own words before starting. Use for /in-your-own-words." +disable-model-invocation: true +--- + +Restate the user's goal and intent in your own words, then wait for them to confirm before you start. diff --git a/pstack/skills/poteto-help/SKILL.md b/pstack/skills/poteto-help/SKILL.md index 575d80fbf..d2b377941 100644 --- a/pstack/skills/poteto-help/SKILL.md +++ b/pstack/skills/poteto-help/SKILL.md @@ -63,6 +63,7 @@ The default answer is `/poteto-mode`, which runs most of the others when its ste | The user wants to | Skill | |---|---| | Do any non-trivial task with rigor | [`/poteto-mode`](../poteto-mode/SKILL.md) | +| Check that the agent understood their goal and the problem before it starts | [`/in-your-own-words`](../in-your-own-words/SKILL.md) | | Know how code works now, or where new code should live | [`/how`](../how/SKILL.md) | | Know why code is shaped this way, or where a number came from | [`/why`](../why/SKILL.md) | | Understand a change or subsystem, explained plainly | [`/teach`](../teach/SKILL.md) | diff --git a/pstack/skills/poteto-help/references/recipes.md b/pstack/skills/poteto-help/references/recipes.md index 73118e2a2..a78942c78 100644 --- a/pstack/skills/poteto-help/references/recipes.md +++ b/pstack/skills/poteto-help/references/recipes.md @@ -5,6 +5,7 @@ Swap in the real paths, skills, and done checks. Informal wording works. ## Understand - `/poteto-mode read . restate the underlying issue in your own words, in plain english.` +- `/in-your-own-words ` - `/poteto-mode investigate why . give me what we know, what data you used, and your best hypotheses. don't change any code yet.` - `use /how to understand . then use /why to find out why it broke recently.` - `/recall my work on from last week, then read .` From d00ba6ee9b26ff3bb084b8a5051ff6436860cbdf Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 09:21:16 -0700 Subject: [PATCH 03/13] feat(pstack): shorten /ketchup replies and build them on real visuals (#595) * feat(pstack): shorten /ketchup replies and build them on real visuals Cap the reply text at 250 words, with Markdown tables counted as text. Show what happened in one status visual with at most three one-line results, give each action item in three lines, and put each item's options and costs in a before-and-after visual. Define a visual as a picture, so a Markdown table no longer counts as one. Bump pstack to 0.15.22. * fix(pstack): keep /ketchup's number check and missing-screenshot mark Check each number the reply shows against its source and say when one is corrected. Put a visible change that has no screenshot in the status visual, marked "No screenshot yet", so the result cap cannot drop it. * chore(pstack): drop version bump --------- Co-authored-by: Cursor Agent --- pstack/README.md | 2 +- pstack/skills/ketchup/SKILL.md | 20 +++++++++++++------- pstack/skills/poteto-help/SKILL.md | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pstack/README.md b/pstack/README.md index 407fe2e8b..1ca3665fa 100644 --- a/pstack/README.md +++ b/pstack/README.md @@ -117,7 +117,7 @@ to keep [`/poteto-mode`](./skills/poteto-mode/SKILL.md) on across turns, pick it | [`/how`](./skills/how/SKILL.md) | you want a walkthrough of how a subsystem works. | | [`/why`](./skills/why/SKILL.md) | you want to know why something was built this way. discovers available MCPs at run time and queries each evidence category in parallel (source control, issue tracker, long-form docs, real-time chat, infra observability, error tracking, analytics warehouse). | | [`/recall`](./skills/recall/SKILL.md) | you're starting or resuming work and want your recent context on a topic rebuilt from your own chat history and the shared record, handed back as a tight current-state brief. | -| [`/ketchup`](./skills/ketchup/SKILL.md) | you want everything in this chat since your last message, in plain words, with an executive summary for each action item so you can make the call. | +| [`/ketchup`](./skills/ketchup/SKILL.md) | you want what changed in this chat since your last message, in a short reply built on real visuals, with just enough context on each action item to make the call. | | [`/blast-radius`](./skills/blast-radius/SKILL.md) | you have a small-looking change and want to know what else it could break, with the one fact it's safe because of proven by running code, not asserted. | | [`/architect`](./skills/architect/SKILL.md) | you're about to write code that crosses a function boundary and want the caller's usage, types, and module shape settled first. | | [`/arena`](./skills/arena/SKILL.md) | you want N parallel attempts at the same thing, then to grab the best parts of each. | diff --git a/pstack/skills/ketchup/SKILL.md b/pstack/skills/ketchup/SKILL.md index 1c63f43f5..475501928 100644 --- a/pstack/skills/ketchup/SKILL.md +++ b/pstack/skills/ketchup/SKILL.md @@ -1,27 +1,33 @@ --- name: ketchup -description: "Catch the user up on everything in this chat since their last message, in plain words. Give each action item an executive summary with enough context to make the call. Use for /ketchup." +description: "Catch the user up on what changed in this chat since their last message, in a short reply built on real visuals. Give each action item just enough context to make the call. Use for /ketchup." disable-model-invocation: true --- # Ketchup -Catch the user up on everything in this chat since the last message they wrote before `/ketchup`. Notifications and subagent results are not their messages. Write in the plain voice of [`/bro`](../bro/SKILL.md), and skip its restating step. Assume the user remembers their last message and nothing after it. Call each PR, branch, or agent by a name they would know, not by a label you invented during the work. +Catch the user up on what changed in this chat since the last message they wrote before `/ketchup`. Notifications and subagent results are not their messages. Write in the plain voice of [`/bro`](../bro/SKILL.md), and skip its restating step. Assume the user remembers their last message and nothing after it. Call each PR, branch, or agent by a name they would know, not by a label you invented during the work. ## Gather what changed -Keep this turn read-only, except for visuals you make for the reply from what you already have. Do not change code, start an app or a job, or post anything to make them. Collect your own work since that message, reports from subagents, and messages from anyone else in this chat. Check the current state of each PR, CI run, or agent this chat touched. Say which are still running and which you could not check. +Keep this turn read-only, except for visuals you make for the reply from what you already have. Do not change code, start an app or a job, or post anything to make them. Collect your own work since that message, reports from subagents, and messages from anyone else in this chat. Check the current state of each PR, CI run, or agent this chat touched. Check each number you will show against the facts it comes from, and say so when you correct one. An action item is a decision or step only the user can take, such as a product choice, an approval, an irreversible action, or access you lack. Include any still waiting on the user, even one raised before their last message. Work you can still do yourself is not an action item. ## Write the reply +Keep the text under 250 words. Markdown tables are text, so their words count. Visuals do not count. A visual replaces the text it shows, so never give a fact in both. Leave out work that changed nothing for the user. + Open with one sentence that says how many action items there are, or that there are none. If nothing happened and nothing waits on the user, say so and stop. -Then say what happened, most important first. Give each result and what it means for the user. Skip steps that changed nothing. Name in one line any work left that you can do yourself. +Then show what happened in one visual: each PR, run, or agent this chat touched, with its state (done, running, failed, or not checked). Under it, give at most three one-line results that change what the user knows or must do. Keep a result's key number in its line unless the visual shows it. Then name in one line any work left that you can do yourself. + +Then give each action item in three lines: the decision, your recommendation and why, and what happens if the user does not answer. Write "unknown" for a part you do not know. Link to the detail. + +Show each action item as a before and after, side by side, in a visual. For a choice, put the current state beside each option, or the options alone when there is no current state. Put the options and what each costs in the visual, not in the text. Skip the visual only for a request for access. -Then give each action item a short executive summary. Cover the decision to make, why it matters now, the options and what each costs, your recommendation, and what happens if the user does not answer. If you do not know a part, say so instead of guessing. Link to the detail when there is one, and write the summary so the user can decide without opening it. +## Visuals -Show each action item as a before and after, side by side. For a choice, put the current state beside each option, or the options alone when there is no current state. Use screenshots or a video when the change is visible. Otherwise use another artifact, such as a table of numbers, a diff, or a diagram. If no visual exists yet, make one. If you cannot, say plainly that none exists. Never invent a number, and never draw or generate a picture of a screen you did not capture. Link only artifacts that exist. +A visual is a picture: a screenshot, a video, a Mermaid diagram, a chart or diagram from `/visualize`, or a generated image. A Markdown table is not a visual, even with a before column and an after column. -Prefer visuals to dense prose. Use a table, a Mermaid diagram, a chart, or a generated image. +Use screenshots or a video when the change is visible. If a visible change has none, add it to the visual of what happened, marked "No screenshot yet." For anything else, draw the visual from facts you have: a Mermaid diagram for states, flows, and structures, and a `/visualize` chart for numbers. Never invent a number, and never draw or generate a picture of a screen you did not capture. Link only artifacts that exist. diff --git a/pstack/skills/poteto-help/SKILL.md b/pstack/skills/poteto-help/SKILL.md index d2b377941..04e36eb61 100644 --- a/pstack/skills/poteto-help/SKILL.md +++ b/pstack/skills/poteto-help/SKILL.md @@ -68,7 +68,7 @@ The default answer is `/poteto-mode`, which runs most of the others when its ste | Know why code is shaped this way, or where a number came from | [`/why`](../why/SKILL.md) | | Understand a change or subsystem, explained plainly | [`/teach`](../teach/SKILL.md) | | Catch up on their own recent work on a topic | [`/recall`](../recall/SKILL.md) | -| Catch up on everything in this chat since their last message, with enough context to decide each action item | [`/ketchup`](../ketchup/SKILL.md) | +| Catch up on what changed in this chat since their last message, in a short reply with visuals and enough context to decide each action item | [`/ketchup`](../ketchup/SKILL.md) | | Know what a small diff could break outside itself | [`/blast-radius`](../blast-radius/SKILL.md) | | Settle types and module shape before code that crosses a function boundary | [`/architect`](../architect/SKILL.md) | | Get several attempts at one brief, merged into the best one | [`/arena`](../arena/SKILL.md) | From 36cecbf2b5089b275254d59cb4612d6f96f00fbd Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 09:22:38 -0700 Subject: [PATCH 04/13] feat(pstack): propose a cheap always-on signal after a deep debugging dig (#604) * feat(pstack): propose a cheap always-on signal after a deep debugging dig * chore(pstack): drop version bump --------- Co-authored-by: Cursor Agent --- pstack/skills/principle-fix-root-causes/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pstack/skills/principle-fix-root-causes/SKILL.md b/pstack/skills/principle-fix-root-causes/SKILL.md index 7e5a419ae..5291682d0 100644 --- a/pstack/skills/principle-fix-root-causes/SKILL.md +++ b/pstack/skills/principle-fix-root-causes/SKILL.md @@ -17,6 +17,7 @@ When debugging, do not fix symptoms. Trace every problem to its root cause and f - If a workaround needs a paragraph-long comment to justify it, the code is wrong (fix the code, not the comment) - Check for the pattern, not just the instance (grep for the same pattern, fix all instances) - When stuck, instrument. Don't guess (add logging, read the actual error) +- If the deciding fact took a deep dig to find, propose the cheap, always-on signal that would have shown it at first look **Restart bugs: suspect state before code** From 3177dab5d4057724e6a02a198596664707795444 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 09:25:20 -0700 Subject: [PATCH 05/13] chore(pstack): bump to 0.15.22 (#605) --- pstack/.cursor-plugin/plugin.json | 2 +- pstack/plugin.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pstack/.cursor-plugin/plugin.json b/pstack/.cursor-plugin/plugin.json index 40b963374..7dc5d3533 100644 --- a/pstack/.cursor-plugin/plugin.json +++ b/pstack/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "pstack", "displayName": "pstack", - "version": "0.15.21", + "version": "0.15.22", "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.", "author": { "name": "Lauren Tan" diff --git a/pstack/plugin.json b/pstack/plugin.json index 4376304ac..2ed3fc463 100644 --- a/pstack/plugin.json +++ b/pstack/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "pstack", - "version": "0.15.21", + "version": "0.15.22", "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.", "author": { "name": "Lauren Tan" From dbf49813a39c1a5389c5e28cb8e4c1c2379e2454 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 09:35:31 -0700 Subject: [PATCH 06/13] refactor(pstack): reword /in-your-own-words around goals and the problem (#606) * refactor(pstack): reword /in-your-own-words around goals and the problem * chore(pstack): bump to 0.15.23 --------- Co-authored-by: Cursor Agent --- pstack/.cursor-plugin/plugin.json | 2 +- pstack/plugin.json | 2 +- pstack/skills/in-your-own-words/SKILL.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pstack/.cursor-plugin/plugin.json b/pstack/.cursor-plugin/plugin.json index 7dc5d3533..0da22406a 100644 --- a/pstack/.cursor-plugin/plugin.json +++ b/pstack/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "pstack", "displayName": "pstack", - "version": "0.15.22", + "version": "0.15.23", "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.", "author": { "name": "Lauren Tan" diff --git a/pstack/plugin.json b/pstack/plugin.json index 2ed3fc463..5394623e2 100644 --- a/pstack/plugin.json +++ b/pstack/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "pstack", - "version": "0.15.22", + "version": "0.15.23", "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.", "author": { "name": "Lauren Tan" diff --git a/pstack/skills/in-your-own-words/SKILL.md b/pstack/skills/in-your-own-words/SKILL.md index f30d03e29..4b2a476a9 100644 --- a/pstack/skills/in-your-own-words/SKILL.md +++ b/pstack/skills/in-your-own-words/SKILL.md @@ -1,7 +1,7 @@ --- name: in-your-own-words -description: "Restate the user's goal and intent in your own words before starting. Use for /in-your-own-words." +description: "Restate in your own words what you think the user's goals are and what problem they're trying to solve. Use for /in-your-own-words." disable-model-invocation: true --- -Restate the user's goal and intent in your own words, then wait for them to confirm before you start. +Restate in your own words what you think the user's goals are and what problem they're trying to solve. From c86f349b102feb91f1966f8a6a60882a4e601b50 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 14:39:54 -0700 Subject: [PATCH 07/13] refactor(pstack): make /ketchup read like /bro in 150 words (#609) * refactor(pstack): make /ketchup read like /bro in 150 words Co-authored-by: lauren * refactor(pstack): name the on-screen change and keep irreversible steps in /ketchup Co-authored-by: lauren * fix(pstack): keep /ketchup on /bro's voice and fit unasked irreversible steps into the reply format Co-authored-by: lauren --------- Co-authored-by: Cursor Agent --- pstack/README.md | 2 +- pstack/skills/ketchup/SKILL.md | 26 +++++++++++++++----------- pstack/skills/poteto-help/SKILL.md | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/pstack/README.md b/pstack/README.md index 1ca3665fa..6314039f9 100644 --- a/pstack/README.md +++ b/pstack/README.md @@ -117,7 +117,7 @@ to keep [`/poteto-mode`](./skills/poteto-mode/SKILL.md) on across turns, pick it | [`/how`](./skills/how/SKILL.md) | you want a walkthrough of how a subsystem works. | | [`/why`](./skills/why/SKILL.md) | you want to know why something was built this way. discovers available MCPs at run time and queries each evidence category in parallel (source control, issue tracker, long-form docs, real-time chat, infra observability, error tracking, analytics warehouse). | | [`/recall`](./skills/recall/SKILL.md) | you're starting or resuming work and want your recent context on a topic rebuilt from your own chat history and the shared record, handed back as a tight current-state brief. | -| [`/ketchup`](./skills/ketchup/SKILL.md) | you want what changed in this chat since your last message, in a short reply built on real visuals, with just enough context on each action item to make the call. | +| [`/ketchup`](./skills/ketchup/SKILL.md) | you want what changed in this chat since your last message, in a few plain sentences and small visuals, with just enough context on each action item to make the call. | | [`/blast-radius`](./skills/blast-radius/SKILL.md) | you have a small-looking change and want to know what else it could break, with the one fact it's safe because of proven by running code, not asserted. | | [`/architect`](./skills/architect/SKILL.md) | you're about to write code that crosses a function boundary and want the caller's usage, types, and module shape settled first. | | [`/arena`](./skills/arena/SKILL.md) | you want N parallel attempts at the same thing, then to grab the best parts of each. | diff --git a/pstack/skills/ketchup/SKILL.md b/pstack/skills/ketchup/SKILL.md index 475501928..b206a42f3 100644 --- a/pstack/skills/ketchup/SKILL.md +++ b/pstack/skills/ketchup/SKILL.md @@ -1,33 +1,37 @@ --- name: ketchup -description: "Catch the user up on what changed in this chat since their last message, in a short reply built on real visuals. Give each action item just enough context to make the call. Use for /ketchup." +description: "Catch the user up on what changed in this chat since their last message, in a few plain sentences and small visuals. Give each action item just enough context to make the call. Use for /ketchup." disable-model-invocation: true --- # Ketchup -Catch the user up on what changed in this chat since the last message they wrote before `/ketchup`. Notifications and subagent results are not their messages. Write in the plain voice of [`/bro`](../bro/SKILL.md), and skip its restating step. Assume the user remembers their last message and nothing after it. Call each PR, branch, or agent by a name they would know, not by a label you invented during the work. +Catch the user up on what changed in this chat since the last message they wrote before `/ketchup`. Notifications and subagent results are not their messages. Assume the user remembers their last message and nothing after it. + +Use [`/bro`](../bro/SKILL.md)'s voice, not its task: plain words, short sentences, no jargon, like one person catching up another. Say what changed for the user, not how you did it. Say "the tests pass", not "CI is green". Leave out hashes, file paths, commands, build numbers, and test counts unless the user needs one to act. Call each PR, branch, or agent by a name the user would know, not by a label you invented. If a fact needs context to make sense, give the context or leave the fact out. ## Gather what changed -Keep this turn read-only, except for visuals you make for the reply from what you already have. Do not change code, start an app or a job, or post anything to make them. Collect your own work since that message, reports from subagents, and messages from anyone else in this chat. Check the current state of each PR, CI run, or agent this chat touched. Check each number you will show against the facts it comes from, and say so when you correct one. +Keep this turn read-only. Make visuals only from what you already have: do not change code, start an app or a job, or post anything. Collect your own work since that message, reports from subagents, and messages from anyone else in this chat. Check the current state of each PR, CI run, or agent this chat touched. Check each number you will show against the facts it comes from, and say so when you correct one. -An action item is a decision or step only the user can take, such as a product choice, an approval, an irreversible action, or access you lack. Include any still waiting on the user, even one raised before their last message. Work you can still do yourself is not an action item. +An action item is a decision or step only the user can take, such as a product choice, an approval, an irreversible action, or access you lack. Include any still waiting on the user, even one raised before their last message. Work you can still do yourself is not an action item. Never make up a call to fill the reply. A step the user did not ask for and the work does not need is not an action item, unless it is irreversible and waiting on the user: give it one action line, no visual, and skip what happens if the user does not answer. ## Write the reply -Keep the text under 250 words. Markdown tables are text, so their words count. Visuals do not count. A visual replaces the text it shows, so never give a fact in both. Leave out work that changed nothing for the user. +Keep the whole reply well under 150 words, counting the words inside visuals and tables. Never give a fact in both the text and a visual. Leave out work that changed nothing for the user. When words run short, cut from what happened before you cut from the action items. -Open with one sentence that says how many action items there are, or that there are none. If nothing happened and nothing waits on the user, say so and stop. +Open with one sentence that says how many things need the user, or that nothing does. If nothing happened and nothing waits on the user, say so and stop. -Then show what happened in one visual: each PR, run, or agent this chat touched, with its state (done, running, failed, or not checked). Under it, give at most three one-line results that change what the user knows or must do. Keep a result's key number in its line unless the visual shows it. Then name in one line any work left that you can do yourself. +Then show what happened in one visual of at most four boxes. Give each thing the user asked for a box that says what came of it and whether it is done, still running, failed, or not checked. Write each box the way you would say it out loud: "Search was slow from a missing index", not "Search review: done" or "Slow search: missing index". Name any bug found and fixed on the way. Each box must stay true when read alone. Never mark something done that still waits on the user. Under the visual, give at most three short lines that change what the user knows or must do. Then name in one line any work left that you can do yourself. -Then give each action item in three lines: the decision, your recommendation and why, and what happens if the user does not answer. Write "unknown" for a part you do not know. Link to the detail. +Then give each action item two lines, or one for an unasked irreversible step: the decision with your recommendation and why, then what happens if the user does not answer. Ask the call the user owns, such as merge, approve, or pick. A step you would take first goes in your recommendation. So does anything that must happen first, and anything that cannot be undone. Write "unknown" for a part you do not know. Link to the detail. -Show each action item as a before and after, side by side, in a visual. For a choice, put the current state beside each option, or the options alone when there is no current state. Put the options and what each costs in the visual, not in the text. Skip the visual only for a request for access. +Show each action item as a before and after, side by side, in a visual. For a choice, put the current state beside each option, or the options alone when there is no current state. Put every option the work weighed in the visual, each with what it gets the user and what it costs. Keep them out of the text. Skip the visual only for access requests and unasked irreversible steps. ## Visuals -A visual is a picture: a screenshot, a video, a Mermaid diagram, a chart or diagram from `/visualize`, or a generated image. A Markdown table is not a visual, even with a before column and an after column. +A visual is a picture: a screenshot, a video, a Mermaid diagram, a chart or diagram from `/visualize`, or a generated image. A Markdown table is not a visual, even with before and after columns. + +Keep each visual small enough to read at a glance: at most six boxes, with at most eight words in each, not counting "No screenshot yet." Write labels as plain words a person would say, not tags like "Tests: pass". Group small items into one box. A fact that needs a sentence belongs in the text, or nowhere. -Use screenshots or a video when the change is visible. If a visible change has none, add it to the visual of what happened, marked "No screenshot yet." For anything else, draw the visual from facts you have: a Mermaid diagram for states, flows, and structures, and a `/visualize` chart for numbers. Never invent a number, and never draw or generate a picture of a screen you did not capture. Link only artifacts that exist. +Use screenshots or a video when the change is visible. If a visible change has none, mark the box it belongs to "No screenshot yet," and say in a line under the visual what changed on screen. For anything else, draw the visual from facts you have: a Mermaid diagram for states, flows, and structures, and a `/visualize` chart for numbers. Never invent a number, and never draw or generate a picture of a screen you did not capture. Link only artifacts that exist. diff --git a/pstack/skills/poteto-help/SKILL.md b/pstack/skills/poteto-help/SKILL.md index 04e36eb61..cbe18c83c 100644 --- a/pstack/skills/poteto-help/SKILL.md +++ b/pstack/skills/poteto-help/SKILL.md @@ -68,7 +68,7 @@ The default answer is `/poteto-mode`, which runs most of the others when its ste | Know why code is shaped this way, or where a number came from | [`/why`](../why/SKILL.md) | | Understand a change or subsystem, explained plainly | [`/teach`](../teach/SKILL.md) | | Catch up on their own recent work on a topic | [`/recall`](../recall/SKILL.md) | -| Catch up on what changed in this chat since their last message, in a short reply with visuals and enough context to decide each action item | [`/ketchup`](../ketchup/SKILL.md) | +| Catch up on what changed in this chat since their last message, in a few plain sentences and small visuals, with enough context to decide each action item | [`/ketchup`](../ketchup/SKILL.md) | | Know what a small diff could break outside itself | [`/blast-radius`](../blast-radius/SKILL.md) | | Settle types and module shape before code that crosses a function boundary | [`/architect`](../architect/SKILL.md) | | Get several attempts at one brief, merged into the best one | [`/arena`](../arena/SKILL.md) | From e06323e81337750bad6725c26ce2f6e772794705 Mon Sep 17 00:00:00 2001 From: djiang-jq Date: Tue, 13 Oct 2026 17:31:58 -0700 Subject: [PATCH 08/13] feat(third_party): add HighLevel plugin --- .cursor-plugin/marketplace.json | 5 ++ README.md | 1 + .../highlevel/.cursor-plugin/plugin.json | 39 +++++++++++ third_party/highlevel/CHANGELOG.md | 9 +++ third_party/highlevel/LICENSE | 21 ++++++ third_party/highlevel/README.md | 64 ++++++++++++++++++ third_party/highlevel/assets/logo.png | Bin 0 -> 4231 bytes third_party/highlevel/mcp.json | 8 +++ 8 files changed, 147 insertions(+) create mode 100644 third_party/highlevel/.cursor-plugin/plugin.json create mode 100644 third_party/highlevel/CHANGELOG.md create mode 100644 third_party/highlevel/LICENSE create mode 100644 third_party/highlevel/README.md create mode 100644 third_party/highlevel/assets/logo.png create mode 100644 third_party/highlevel/mcp.json diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 824194e9a..a16b50cdc 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -532,6 +532,11 @@ "grokbot": "0.49.0", "sand": "0.49.0" } + }, + { + "name": "highlevel", + "source": "third_party/highlevel", + "description": "Work with contacts, conversations, opportunities, calendars, and payments." } ] } diff --git a/README.md b/README.md index 106f7973f..c04e06cb4 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ Official Cursor plugins for popular developer tools, frameworks, and SaaS produc | `workday` | [Workday](third_party/workday/) | Cursor | Integrations | Look up workers, organizations, time off, payroll, and recruiting data in Workday. | | `square` | [Square](third_party/square/) | Cursor | Integrations | Work with payments, orders, catalog, customers, and invoices. | | `tailscale` | [Tailscale](third_party/tailscale/) | Cursor | Integrations | Let Grok Bot reach MCP servers on your private Tailscale network. Sign in to Tailscale to connect your tailnet; Grok Bot joins it as one tagged device and only reaches the hosts you grant to tag:grokbot. | +| `highlevel` | [HighLevel](third_party/highlevel/) | Cursor | Integrations | Work with contacts, conversations, opportunities, calendars, and payments. | Author values match each plugin’s `plugin.json` `author.name` (Cursor lists `plugins@cursor.com` in the manifest). ## Repository structure diff --git a/third_party/highlevel/.cursor-plugin/plugin.json b/third_party/highlevel/.cursor-plugin/plugin.json new file mode 100644 index 000000000..5045081cd --- /dev/null +++ b/third_party/highlevel/.cursor-plugin/plugin.json @@ -0,0 +1,39 @@ +{ + "name": "highlevel", + "displayName": "HighLevel", + "version": "1.0.0", + "minClientVersions": { + "cursor": "3.13.0" + }, + "description": "Work with contacts, conversations, opportunities, calendars, and payments.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "homepage": "https://help.leadconnectorhq.com/support/solutions/articles/155000008185", + "repository": "https://github.com/cursor/plugins", + "license": "MIT", + "logo": "assets/logo.png", + "keywords": [ + "highlevel", + "gohighlevel", + "leadconnector", + "crm", + "contacts", + "conversations", + "opportunities", + "pipelines", + "calendars", + "appointments", + "marketing", + "mcp" + ], + "category": "integrations", + "tags": [ + "highlevel", + "crm", + "marketing", + "mcp" + ], + "mcpServers": "./mcp.json" +} diff --git a/third_party/highlevel/CHANGELOG.md b/third_party/highlevel/CHANGELOG.md new file mode 100644 index 000000000..8655c9fad --- /dev/null +++ b/third_party/highlevel/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this plugin will be documented here. + +## 1.0.0 — initial release + +- Added the `highlevel` MCP server pointing at `https://services.leadconnectorhq.com/mcp/`. +- Auth uses OAuth — no Private Integration Token or location ID to configure. +- Logo: HighLevel's official arrow mark, from the gohighlevel.com wordmark. diff --git a/third_party/highlevel/LICENSE b/third_party/highlevel/LICENSE new file mode 100644 index 000000000..ca2bba771 --- /dev/null +++ b/third_party/highlevel/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Cursor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/highlevel/README.md b/third_party/highlevel/README.md new file mode 100644 index 000000000..2c82ddb9a --- /dev/null +++ b/third_party/highlevel/README.md @@ -0,0 +1,64 @@ +# HighLevel + +Cursor plugin that connects agents to [HighLevel](https://www.gohighlevel.com) (also branded LeadConnector) through HighLevel's official remote [Model Context Protocol](https://modelcontextprotocol.io/) server. + +Find and update CRM data in a HighLevel sub-account — contacts, conversations and messages, opportunities and pipelines, calendars and appointments, payments, blogs, email templates, and social posts. + +## Install + +1. Open **Cursor Settings → Plugins**. +2. Search for **HighLevel**. +3. Click **Install**, then complete the LeadConnector sign-in prompt. + +Or run `/add-plugin highlevel` in chat. + +## MCP + +```json +{ + "mcpServers": { + "highlevel": { + "type": "http", + "url": "https://services.leadconnectorhq.com/mcp/" + } + } +} +``` + +Auth is OAuth with Dynamic Client Registration and PKCE. Cursor registers itself and opens the LeadConnector sign-in when the plugin connects: sign in, choose the sub-account (location) to expose, and approve. There is no Private Integration Token or `locationId` header to configure. + +## What agents can do + +Tools depend on the scopes granted to the connection. HighLevel's docs list tools in these areas: + +| Area | Example tools | +| --- | --- | +| Contacts | `contacts_get-contacts`, `contacts_get-contact`, `contacts_create-contact`, `contacts_update-contact`, `contacts_upsert-contact`, `contacts_add-tags`, `contacts_remove-tags`, `contacts_get-all-tasks` | +| Conversations | `conversations_search-conversation`, `conversations_get-messages`, `conversations_send-a-new-message` | +| Opportunities | `opportunities_search-opportunity`, `opportunities_get-pipelines`, `opportunities_get-opportunity`, `opportunities_update-opportunity` | +| Calendars | `calendars_get-calendar-events`, `calendars_get-appointment-notes` | +| Locations | `locations_get-location`, `locations_get-custom-fields` | +| Payments | `payments_get-order-by-id`, `payments_list-transactions` | +| Blogs | `blogs_get-blogs`, `blogs_get-blog-post`, `blogs_create-blog-post`, `blogs_update-blog-post` | +| Email templates | `emails_fetch-template`, `emails_create-template` | +| Social posts | `socialmediaposting_get-posts`, `socialmediaposting_create-post`, `socialmediaposting_edit-post`, `socialmediaposting_get-social-media-statistics` | + +The hosted runtime is the source of truth for tool names and schemas. + +## Notes + +- The server can write as well as read — including sending messages, updating contacts and opportunities, and creating posts. Approve only the access agents need, and test on a non-production sub-account first. +- Each connection works against the sub-account chosen at sign-in. +- HighLevel also publishes client-specific OAuth endpoints for Claude (`/mcp/anthropic/v2`) and OpenAI clients (`/mcp/openai/v2/`) that expose a broader operation catalog through a small set of unified tools. This plugin uses the universal endpoint. + +## Docs + +- MCP server help article: https://help.leadconnectorhq.com/support/solutions/articles/155000008185 +- Developer docs: https://marketplace.gohighlevel.com/docs/other/mcp/ +- Server URL: https://services.leadconnectorhq.com/mcp/ + +Logo is HighLevel's official arrow mark, taken from the wordmark on gohighlevel.com. + +## License + +MIT diff --git a/third_party/highlevel/assets/logo.png b/third_party/highlevel/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..907fd6d2f8837fdd4a44b979667171c1adc829de GIT binary patch literal 4231 zcmc&&`8(9z`#*1c2wB2I5lKY$Wd;ppjSwRx%UH4{ls((nLbmKf)<_JIr7T$nAz>2F zH1_qejXl!H$akLa=O6g|_PNe=&bhApocn&i?)$pW>ptflZ)&V}n)xqg0KjPjeQh)9 zjQh7UF;GXdDGx^K#Nc44rwva2ZAEQw(gEO5H_+BX2Ig%}2L~dT@)>uwxASrg73meZA{IZt6b2iEbm&}lieOm~R_ALY3YYJ(QOgyli@yS2K73R0nP8bE4TP@q@C8HMKYwtXi{@30 z=Vz=K3+^-~=Hfxgub#qJw1EWzyDn~VVlBIh zjM;ME5VYRb3&r&M?jH@=hOXbXP!m+IHAX*IckP-Eqa%w7>UB=@Cs;vNZVeQ8 zCfk-GL7(wY+l(E-*3lLlQHz9BBVotZ6bU%febvax!Ov72iu7KLqlT+T3nu+8NsYlT zBZ;}G4~wMCfv2_-+6Fvh^JY4YtDi!DOf(d0*;AzNVCzbe+wn{Pit{%ajYP=F6H#|x zfejw$p()*G?Fe7SxGy&x)x_0&(EtYN!v zIG9X)=mG>33pRtSA_G7xzwTS!NP$EZLo4@okI#WoMGZi(^E3!!7XCU{dj*i!w3G^&W!R52wUHl1*R@DBoTF~?UBZoRhH+RI(?~YA7H(Z| z!yZ2G-`l^uNk7LxmufVyu&pei>`gaoJQ>1|pEUD9KnSD7jH?`vuDID^4)WXGq=B9( zOV>mFN4Ym^wteWrOHeb33y-j&=$!NBvhNdKOx+pbG+MvZ*T91osHMZLn^Lj14e5OBqe{i&+uHz4p7S}Oj^*<8Let0$Q~U!e9c*2ROQ(yz`&z4_WGf|gN_u!+wmmvFnDUq+Y% z&FnO3l3)Ib=?&^hxJ%o0$!TW6Bxu>=R2nu4^J-n?Bwe%DliP`AdXzM2>+(1HnKUvP zC4E^md@mq?QDkf|@7wH+uYAZ6vm-_MfL&T3N0u-#OtP=Beb!3jO}7p(&2Ey@1wBqv zsY*3;tdef0zMrXE?!M02+tx=>E8yGEjIwemRz)jKtTVl;E zL(qtwv;&hnb*a&MDVdwA#7li))I{5n{rr70-tLB=UA@PtYvoU##y+)k9wT>k9!qHz zQj;cx{oOxQFD4^GLNYHcB$9-uEkc_9_UrogM_xEP*`-XV;jj@goZWX&0jA>gaDlGg zwRKF6yv((j-ePkRSs=9OM6HmeWP9k=OMTZXH~v-zrA)#lZI?}{fQFiKje>#@yh!F<7{@v$TA z2^m^c{yQz)GSeWDCM;rPP>^_jiH_5Guy~F8%|KouM$Vr(;`-YMlZESt51Ai}{`uHu zv*$TuMwv`D9?B4?O!|D(_Ha|-tcJUzpAZtJaii_qr_|BZ#Wh6A9(;epdJ+5C^0BFI(*6*AvG%dH;o^>ti0 z-Dg3qhr{{-GUqjju!J6rD`E%kIS zA}sTD_6Oy@Xh?*QV@9jKos{E8?6PD4ScQN5UZsA`kS*Pcm#6t=Y}H=@{7)KgVC(nw zbh!a;CDx$QrQkQe<`ZXP zJBP6KaRp0*(giPlZuN)L{9O$<^pgJofUK`;*ItrZO447sg%nM{tOzEL<$>d$=Nv6o zWYgF*F^ed%@{H7y2gq=26JC|)gyGp z^$~Y#*NmFKcsQ3>zm*MN3hYoGe30`s%*lf4H#Ldsd1uM}vD%azk`ym?Z1D9dc=&F> z;;oAf$M@`L@}Eo%^bR(Bu^*|r0svXOpqcc6M@Wb$wSTHi^IVF-MNUS(siULNNb%I+ zgKT1cg}!6MzFIKmZzVsn)(cpn(pF`a6aayS4qV@BJbjQB6~;8=b+z8QELE^WHoS(J zGjN{>I~jFpfAP%qW^);j#R?(W?6;xhLl)qB?vjNK8drvoM=uKF7Ju(HaIQ{tOcc}o zUTrnY+;j1Rkb6YGtjA^u&|40vxAC@)=rdGNazb7b`&0#r*Hd5TN7c z9GzV9`?jW=B+;S|u_8Z6!0(A~o&(te8p=?NLDCFcbcsUZ`AK{!V5nfyx$^$vCatN* zf+9WZ#5#N5a*k^3TdJC=tx%h!Rs%aOPSHq(4#yTAx{<3mnEA65L~)lV7#qlM(#xtR zjV?#kPV+vudD#oAPvb^$Q085E9=h34Bo4L5Y;Adu(juGdpK+tml$Q<)&;)ytzCCMz zS@+8b?+thizkDt?3WWTu9oKhIK@T8ZpgHHcAzay5wgqFqwi!B1lBH*v!;!bDmTdk* zHNKHj+%zRwQCJF&P0uG=JdDl<&#?HBC_#=65=-BbbI`Fc{c4G(jfW>^mJLPXDoqC` zQ{R$*Md(HPRoZY>eF)xt$CmEo0igz5fOs>*d{ zAk6W6)@ZH}c$eeT7(QwHMR@zRk%aG{j%eseivt+4*q9^r-WgSJK-4?2_TwOU*nn`= z_PlZb1VjvOKcnW_(-e}hj=dNM_6oLC&kt5~e}1o8y^#7d!!e(# z8M>i{^(jq%^oM+VwPd3_q4DVUg}iDiAIHs5McVj0`x|94 z2X0$515-S=^Gujg%D28gwj|(v6EDC2WOJtHuoSFm7ES#i1>r{w7@qmp^?ij z8~>23Px5rp|CS}{nHm|qEngiqjGirsC>vjJO3yTaxQnNX zOMbXWT^+G*rh?iqVlLu`i^W|P{=M%PPHfn{Lhr$ zy>@a7o9R?QLy+lDM^dU?L;XUum r=8y_+PR^;=w|n}UERT`iC%ju%q@EC2%?W9{nesL1~V0vp~r literal 0 HcmV?d00001 diff --git a/third_party/highlevel/mcp.json b/third_party/highlevel/mcp.json new file mode 100644 index 000000000..105b405d5 --- /dev/null +++ b/third_party/highlevel/mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "highlevel": { + "type": "http", + "url": "https://services.leadconnectorhq.com/mcp/" + } + } +} From 9b079e3ac456e6b4b82728e8c5bfd8cfcf078b40 Mon Sep 17 00:00:00 2001 From: djiang-jq Date: Tue, 13 Oct 2026 19:14:12 -0700 Subject: [PATCH 09/13] feat(highlevel): use HighLevel's Cursor MCP endpoint --- .../highlevel/.cursor-plugin/plugin.json | 2 +- third_party/highlevel/CHANGELOG.md | 4 +++ third_party/highlevel/README.md | 28 +++++++++---------- third_party/highlevel/mcp.json | 2 +- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/third_party/highlevel/.cursor-plugin/plugin.json b/third_party/highlevel/.cursor-plugin/plugin.json index 5045081cd..92f633875 100644 --- a/third_party/highlevel/.cursor-plugin/plugin.json +++ b/third_party/highlevel/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "highlevel", "displayName": "HighLevel", - "version": "1.0.0", + "version": "1.1.0", "minClientVersions": { "cursor": "3.13.0" }, diff --git a/third_party/highlevel/CHANGELOG.md b/third_party/highlevel/CHANGELOG.md index 8655c9fad..3e8a9220e 100644 --- a/third_party/highlevel/CHANGELOG.md +++ b/third_party/highlevel/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this plugin will be documented here. +## 1.1.0 + +- Switched the MCP server URL to HighLevel's Cursor endpoint, `https://services.leadconnectorhq.com/mcp/cursor/v2`, which exposes the unified `list_locations`, `search_operations`, `describe_operation`, and `execute_operation` tools. + ## 1.0.0 — initial release - Added the `highlevel` MCP server pointing at `https://services.leadconnectorhq.com/mcp/`. diff --git a/third_party/highlevel/README.md b/third_party/highlevel/README.md index 2c82ddb9a..9273262f0 100644 --- a/third_party/highlevel/README.md +++ b/third_party/highlevel/README.md @@ -19,7 +19,7 @@ Or run `/add-plugin highlevel` in chat. "mcpServers": { "highlevel": { "type": "http", - "url": "https://services.leadconnectorhq.com/mcp/" + "url": "https://services.leadconnectorhq.com/mcp/cursor/v2" } } } @@ -29,33 +29,31 @@ Auth is OAuth with Dynamic Client Registration and PKCE. Cursor registers itself ## What agents can do -Tools depend on the scopes granted to the connection. HighLevel's docs list tools in these areas: +HighLevel's client-specific `/v2` endpoints expose a small set of unified tools that reach the full operation catalog: -| Area | Example tools | +| Tool | Purpose | | --- | --- | -| Contacts | `contacts_get-contacts`, `contacts_get-contact`, `contacts_create-contact`, `contacts_update-contact`, `contacts_upsert-contact`, `contacts_add-tags`, `contacts_remove-tags`, `contacts_get-all-tasks` | -| Conversations | `conversations_search-conversation`, `conversations_get-messages`, `conversations_send-a-new-message` | -| Opportunities | `opportunities_search-opportunity`, `opportunities_get-pipelines`, `opportunities_get-opportunity`, `opportunities_update-opportunity` | -| Calendars | `calendars_get-calendar-events`, `calendars_get-appointment-notes` | -| Locations | `locations_get-location`, `locations_get-custom-fields` | -| Payments | `payments_get-order-by-id`, `payments_list-transactions` | -| Blogs | `blogs_get-blogs`, `blogs_get-blog-post`, `blogs_create-blog-post`, `blogs_update-blog-post` | -| Email templates | `emails_fetch-template`, `emails_create-template` | -| Social posts | `socialmediaposting_get-posts`, `socialmediaposting_create-post`, `socialmediaposting_edit-post`, `socialmediaposting_get-social-media-statistics` | +| `list_locations` | List the sub-accounts this connection can use, or return the one bound sub-account | +| `search_operations` | Discover operations by intent, including record searches and create, update, or delete actions | +| `describe_operation` | Inspect an operation's inputs, example payload, required scopes, and safety information | +| `execute_operation` | Run an operation, subject to OAuth scopes, permissions, and built-in safety checks | + +Behind those tools sit more than 550 operations across 38 domains, including contacts, conversations and messages, opportunities and pipelines, calendars and appointments, payments, products and store, invoices and estimates, social planner, blogs, emails, forms and surveys, funnels, workflows, Voice AI, custom objects and fields, and users. The operations available depend on the scopes granted. The hosted runtime is the source of truth for tool names and schemas. ## Notes - The server can write as well as read — including sending messages, updating contacts and opportunities, and creating posts. Approve only the access agents need, and test on a non-production sub-account first. -- Each connection works against the sub-account chosen at sign-in. -- HighLevel also publishes client-specific OAuth endpoints for Claude (`/mcp/anthropic/v2`) and OpenAI clients (`/mcp/openai/v2/`) that expose a broader operation catalog through a small set of unified tools. This plugin uses the universal endpoint. +- Every request runs against one sub-account chosen at sign-in. Agencies that install with the multi-sub-account option can switch between the selected sub-accounts in the same chat. +- Sensitive and irreversible operations go through additional confirmation and safety checks. +- This plugin uses HighLevel's Cursor endpoint (`/mcp/cursor/v2`). The universal endpoint (`/mcp/`) exposes a smaller set of named tools. ## Docs - MCP server help article: https://help.leadconnectorhq.com/support/solutions/articles/155000008185 - Developer docs: https://marketplace.gohighlevel.com/docs/other/mcp/ -- Server URL: https://services.leadconnectorhq.com/mcp/ +- Server URL: https://services.leadconnectorhq.com/mcp/cursor/v2 Logo is HighLevel's official arrow mark, taken from the wordmark on gohighlevel.com. diff --git a/third_party/highlevel/mcp.json b/third_party/highlevel/mcp.json index 105b405d5..94d111784 100644 --- a/third_party/highlevel/mcp.json +++ b/third_party/highlevel/mcp.json @@ -2,7 +2,7 @@ "mcpServers": { "highlevel": { "type": "http", - "url": "https://services.leadconnectorhq.com/mcp/" + "url": "https://services.leadconnectorhq.com/mcp/cursor/v2" } } } From ce649c03ac3b75b6ae80cbd15b7e2c536caedb5a Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 21:34:54 -0700 Subject: [PATCH 10/13] feat(pstack): match subagent effort to the job, drop Graphite from orchestrate, and cut playbooks to principles (#613) * feat(pstack): match each subagent's reasoning level and speed to the job Add a Match the model to the job rule to poteto-mode's Subagents section. Each spawn starts from the role's configured slug, moves the effort level for how hard the job is, and uses fast mode only when the job is urgent. Verification keeps the swarm workers model. Point the routed skills and playbooks that spawn subagents at the rule. * fix(pstack): put cursor-grok and composer slugs in the grok family for arena and interrogate fallbacks A refused configured entry falls back to its family's default. Slugs that start with cursor-grok- or composer- now count as the grok family, so a refused composer entry runs on the grok default instead of the no-match claude default. * feat(pstack): run one full swarm per multi-phase PR and a single verifier for small follow-ups The multi-phase plan skeleton ran the full swarm at every push that changed the patch. Run it once at the code-ready head, and again only for new behavior, a redesign, or a large new diff. A rebase, a conflict resolution, or a minor fix gets one verifier at the new head. * refactor(pstack): cut the shipping playbook to its principles Shipping goes from nine steps to six and states why each rule holds. After a merge, the child PR is retargeted to trunk, its patch (not its commits) moves onto the merge result, and it is pushed with --force-with-lease. The patch-id rule then decides whether its verdict survives. opening-a-pr points stacked children at that step. * refactor(pstack): cut the autopilot playbooks to their principles Autopilot-full and Autopilot-stack keep their rules and drop the procedure around them, and each rule says why it holds. Autopilot-stack now states only where it differs from Autopilot-full. Three rules are new: - Pin --force-with-lease to the SHA you last pushed or took over. - When trunk changed a caller of the patch or something it depends on, run the patch's checks on the merge result before merging. - Hold and report to the root rather than take a third rebase past merge prep. A full swarm runs once at the code-ready head, matching the multi-phase plan. * refactor(pstack): resolve the orchestrate frontier from the PR base chain instead of Graphite orch frontier set now walks the pull request base chain through the checkout's current branch with gh by default, or origin with --forge origin. Workers and babysitters no longer run gt; one stacker owns chain rebases, retargets, and merges. * fix(pstack): treat a Code Review Gate waiting on an owner approval as the gate in watch-pr A completed Code Review Gate check run with an ACTION_REQUIRED conclusion now maps to the owner-approval gate instead of a failed check, so the watcher neither fails nor waits on a human approval. * docs(pstack): say trunk, not main, in the subagent model rule * docs(pstack): drop the routing sentence that contradicts the delegate rule The Subagents section said both that the chat receiving /poteto-mode runs the playbook and that /poteto-mode and poteto-agent route through the same wrapper. A Task subagent has no Task tool of its own, so the first rule holds and the second sentence goes. * fix(pstack): read the worktree audit PR column from Origin when it can gh pr list cannot see Origin pull requests, so the PR column was always empty for repositories on Origin. The audit now reads origin pr list when the Origin CLI can read the repository and falls back to gh otherwise. Both shapes normalize to one lowercase state, and draft pull requests also hold the worktree. * fix(pstack): let the watch-pr fast path classify an owner-approval Code Review Gate Co-authored-by: lauren --------- Co-authored-by: Cursor Agent --- pstack/skills/arena/SKILL.md | 2 +- pstack/skills/automate-me/SKILL.md | 2 +- pstack/skills/figure-it-out/SKILL.md | 2 +- pstack/skills/how/SKILL.md | 2 +- pstack/skills/interrogate/SKILL.md | 4 +- pstack/skills/no-comments/SKILL.md | 2 +- pstack/skills/poteto-mode/SKILL.md | 4 +- .../poteto-mode/playbooks/autonomous-run.md | 2 +- .../poteto-mode/playbooks/autopilot-full.md | 30 +- .../poteto-mode/playbooks/autopilot-stack.md | 14 +- .../skills/poteto-mode/playbooks/bug-fix.md | 2 +- pstack/skills/poteto-mode/playbooks/eval.md | 2 +- .../skills/poteto-mode/playbooks/feature.md | 2 +- .../skills/poteto-mode/playbooks/hillclimb.md | 2 +- .../poteto-mode/playbooks/multi-phase-plan.md | 6 +- .../poteto-mode/playbooks/opening-a-pr.md | 2 +- .../poteto-mode/playbooks/orchestrate.md | 15 +- .../poteto-mode/playbooks/perf-issue.md | 2 +- .../poteto-mode/playbooks/refactoring.md | 2 +- .../playbooks/runtime-forensics.md | 2 +- .../poteto-mode/playbooks/session-pickup.md | 2 +- .../skills/poteto-mode/playbooks/shipping.md | 21 +- .../poteto-mode/playbooks/trace-forensics.md | 2 +- .../poteto-mode/playbooks/worktree-cleanup.md | 2 +- .../poteto-mode/scripts/orch/orch.test.ts | 319 ++++++++++----- .../skills/poteto-mode/scripts/orch/orch.ts | 16 +- .../skills/poteto-mode/scripts/orch/store.ts | 382 +++++++++++------- .../scripts/watch-pr/github.test.ts | 32 ++ .../poteto-mode/scripts/watch-pr/github.ts | 32 +- .../poteto-mode/scripts/worktree-audit.sh | 19 +- pstack/skills/recall/SKILL.md | 2 +- pstack/skills/reflect/SKILL.md | 2 +- pstack/skills/show-me-your-work/SKILL.md | 2 +- pstack/skills/swarm/SKILL.md | 2 +- pstack/skills/why/SKILL.md | 2 +- 35 files changed, 595 insertions(+), 343 deletions(-) diff --git a/pstack/skills/arena/SKILL.md b/pstack/skills/arena/SKILL.md index 58ae9d9a3..b198b9e42 100644 --- a/pstack/skills/arena/SKILL.md +++ b/pstack/skills/arena/SKILL.md @@ -14,7 +14,7 @@ Every candidate gets the same prompt, so the prompt is the contract. 1. State the artifact each candidate is producing. 2. Derive the rubric, 3-6 concrete gradeable criteria for what success looks like on *this* task. It is the picker's tool in Phase D. Candidates only see the task. -3. Pick the runners. Use the `arena runners` line in `~/.cursor/rules/pstack-models.mdc`. If the rule or that line is missing, default to one each on `claude-opus-5-5-xhigh` and `grok-4.7-xhigh-fast`. An `auto` or `inherit-parent` entry in this line or the cross-judge line means the parent model, so omit `model` for it. If the Task tool rejects a configured entry, run that seat on its family's default and say so. Families go by prefix: `claude-*` and `grok-*`. With no family match, use `claude-opus-5-5-xhigh`. If it rejects a default, use the closest valid slug of the same family from its error message. Spawn more runners when the arena covers multiple design directions. Use the same model N times when the work is generation-bound rather than judgment-sensitive. +3. Pick the runners. Use the `arena runners` line in `~/.cursor/rules/pstack-models.mdc`. If the rule or that line is missing, default to one each on `claude-opus-5-5-xhigh` and `grok-4.7-xhigh-fast`. An `auto` or `inherit-parent` entry in this line or the cross-judge line means the parent model, so omit `model` for it. If the Task tool rejects a configured entry, run that seat on its family's default and say so. Families go by prefix: `claude-*`, and `grok-*` with `cursor-grok-*` and `composer-*`. A refused `composer-*` entry runs on `grok-4.7-xhigh-fast`. With no family match, use `claude-opus-5-5-xhigh`. If it rejects a default, use the closest valid slug of the same family from its error message. Spawn more runners when the arena covers multiple design directions. Use the same model N times when the work is generation-bound rather than judgment-sensitive. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. 4. Give each candidate its own output location (a git worktree where possible, otherwise `/tmp/arena-/candidate-/`), per the **separate-before-serializing-shared-state** principle skill. ## Phase B: Fan out diff --git a/pstack/skills/automate-me/SKILL.md b/pstack/skills/automate-me/SKILL.md index cd3636e9a..50971b894 100644 --- a/pstack/skills/automate-me/SKILL.md +++ b/pstack/skills/automate-me/SKILL.md @@ -28,7 +28,7 @@ Update mode changes the rest of the flow: Locate the active workspace's transcripts before fanning out. The system prompt names the workspace's `agent-transcripts/` directory. Use only that path. Don't glob across `~/.cursor/projects/*/`. That crosses workspace boundaries and reads private chats from unrelated projects. -Survey recent agent conversations within that scope for recurring patterns. Run multiple parallel subagents across slices of history (e.g. last 2-4 weeks, split into 3 slices so each has enough material). Each slice mining subagent reads transcripts from the workspace-scoped path the parent provides, looks for the signals below, and returns a short structured list of patterns it saw with evidence pointers. Default signals worth hunting: +Survey recent agent conversations within that scope for recurring patterns. Run multiple parallel subagents across slices of history (e.g. last 2-4 weeks, split into 3 slices so each has enough material). Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. Each slice mining subagent reads transcripts from the workspace-scoped path the parent provides, looks for the signals below, and returns a short structured list of patterns it saw with evidence pointers. Default signals worth hunting: - Response preferences (length, tone, format, "dumb it down" corrections) - Delegation habits (subagents, models, specialized workflows, parallelism) diff --git a/pstack/skills/figure-it-out/SKILL.md b/pstack/skills/figure-it-out/SKILL.md index 195f50b83..0d04c2e69 100644 --- a/pstack/skills/figure-it-out/SKILL.md +++ b/pstack/skills/figure-it-out/SKILL.md @@ -28,7 +28,7 @@ Decompose into atomic, independently-landable units. Sequence riskiest-unknown-f - Build the verification harness before the work, with the baseline captured from the pre-change state, so the check reads as "old value vs new value". - For one-way-door design decisions, run the **architect** skill (it runs **arena**). Skip it for mechanical work whose shape is already concrete. A second arena over a settled design is over-engineering (the **laziness-protocol** principle skill). -- Decide what fans out. Parallelize only across seams, and give each worker its own worktree or branch (the **separate-before-serializing-shared-state** principle skill). Don't over-fan. +- Decide what fans out. Parallelize only across seams, and give each worker its own worktree or branch (the **separate-before-serializing-shared-state** principle skill). Don't over-fan. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. - Write the designed phase list down. That list is what the human reviews. Then execute the design. Add its steps to the todolist as concrete items between the Phase C and Phase D entries. Run each under the Phase C loop discipline, and weave the Phase D log through them, a row as each step lands, rather than saving the whole trail for the end. diff --git a/pstack/skills/how/SKILL.md b/pstack/skills/how/SKILL.md index 775d292f8..67edc340d 100644 --- a/pstack/skills/how/SKILL.md +++ b/pstack/skills/how/SKILL.md @@ -8,7 +8,7 @@ disable-model-invocation: true Explore the codebase to answer "how does X work?" questions. Produce architectural explanations at the level of a senior engineer onboarding onto a subsystem, enough to build a working mental model, not so much that it reads like annotated source code. -Every spawn is a Task subagent with `subagent_type: generalPurpose` and `readonly: true`. Explorers use the `how explorer` line in the `pstack-models.mdc` rule (default `grok-4.7-xhigh-fast`), and every explainer uses the `how explainer` line (default `claude-opus-5-5-xhigh`). Set `model` to that line's value, or to the default if the rule or the line is missing. Leave `model` unset when the value is `auto` or `inherit-parent`. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. +Every spawn is a Task subagent with `subagent_type: generalPurpose` and `readonly: true`. Explorers use the `how explorer` line in the `pstack-models.mdc` rule (default `grok-4.7-xhigh-fast`), and every explainer uses the `how explainer` line (default `claude-opus-5-5-xhigh`). Set `model` to that line's value, or to the default if the rule or the line is missing. Leave `model` unset when the value is `auto` or `inherit-parent`. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. ## 1. Assess complexity diff --git a/pstack/skills/interrogate/SKILL.md b/pstack/skills/interrogate/SKILL.md index 9e3b99a19..94946d22d 100644 --- a/pstack/skills/interrogate/SKILL.md +++ b/pstack/skills/interrogate/SKILL.md @@ -33,9 +33,9 @@ Launch all reviewers in a single message with the Task tool, one per entry on th Each reviewer gets `subagent_type: generalPurpose`, `readonly: true`, and `model` set to its configured entry, or its table default when there is no configured line. Omit `model` for an `auto` or `inherit-parent` entry, so that reviewer runs on the parent model. -If the Task tool rejects a configured entry, run that reviewer on its family's table default and say so. Families go by prefix: `claude-*` and `grok-*`. With no family match, use Reviewer A's default. If it rejects a table default, spawn with the closest equivalent from the valid slugs in its error message (prefer the same family and reasoning tier), and open a separate PR to update the default table. Do not block the review on the slug issue. Never treat an alias entry as a rejected slug or apply either fallback to it. +If the Task tool rejects a configured entry, run that reviewer on its family's table default and say so. Families go by prefix: `claude-*`, and `grok-*` with `cursor-grok-*` and `composer-*`. A refused `composer-*` entry runs on Reviewer B's default. With no family match, use Reviewer A's default. If it rejects a table default, spawn with the closest equivalent from the valid slugs in its error message (prefer the same family and reasoning tier), and open a separate PR to update the default table. Do not block the review on the slug issue. Never treat an alias entry as a rejected slug or apply either fallback to it. -Read `references/reviewer-prompt.md` and fill in the same template for every reviewer with the stated intent, the diff or file contents, the rubric from `references/rubric.md`, and the code-quality lens from `references/code-quality-review.md`. +Read `references/reviewer-prompt.md` and fill in the same template for every reviewer with the stated intent, the diff or file contents, the rubric from `references/rubric.md`, and the code-quality lens from `references/code-quality-review.md`. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. ## Step 4, Synthesize diff --git a/pstack/skills/no-comments/SKILL.md b/pstack/skills/no-comments/SKILL.md index 4b608af0c..a07ce9dfc 100644 --- a/pstack/skills/no-comments/SKILL.md +++ b/pstack/skills/no-comments/SKILL.md @@ -16,7 +16,7 @@ Use the caller's files or diff. Otherwise use the current diff against the base ## Steps -1. Spawn `Task` with `subagent_type: "comment-sicko"`. Pass the scope. Do not restate its rules. +1. Spawn `Task` with `subagent_type: "comment-sicko"`. Pass the scope. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. Do not restate its rules. 2. Inspect its report and diff. Reject application-code edits, scope escapes, exception-protected deletions, misstated `MUST KILL` reasons, and flags that treat kept intentional code as guilty. Reshape flags on our-code surprises stay actionable. Do not restore those comments. A keep survives only with proof it is about something we cannot change. Audit missed scoped lint and TypeScript suppressions. Correctness or safety suppressions stay actionable `MUST KILL`s. Restore deletions only with exact exceptions and scoped proof. Before accepting thin `IMPORTANT` or `do not remove` kills or keeps, run `/how` or `/why` on their symbol. If a kill is ambiguous, do not restore. If a keep is refuted or still ambiguous, delete it. Revert and rerun one rejected report with the failure named. Reject a second, report it open, and fail `/no-comments`. 3. Fix trivial accepted flags directly by deleting a dead path, dropping a parameter, or using the real API. If any fix needs a shape, run `/architect` once for the accepted set and surrounding code. Stop at the sketch. Architect shapes. Step 4 implements. 4. Implement the smallest root-cause fix in scope. Remove every named workaround. If the root cause is out of scope, land the smallest in-scope fix and report the rest open. The **principle-fix-root-causes** and **principle-redesign-from-first-principles** skills guide intent only. Neither authorizes widening the fence nor fixing instances outside it. Never bolt on symptom guards. diff --git a/pstack/skills/poteto-mode/SKILL.md b/pstack/skills/poteto-mode/SKILL.md index fef2b3689..ac3d64ff0 100644 --- a/pstack/skills/poteto-mode/SKILL.md +++ b/pstack/skills/poteto-mode/SKILL.md @@ -90,10 +90,12 @@ Read the leaf skill in full for any principle you apply. Each entry names when i ## Subagents -**Use `subagent_type: "poteto-agent"` for any subagent you spawn inside a playbook step** (code-writing delegates, ad-hoc helpers). The chat that receives `/poteto-mode` runs the playbook. Never spawn a `poteto-agent` to run the playbook itself. `/poteto-mode` and `poteto-agent` route through the same wrapper. Routed workflow skills (`how`, `why`, `interrogate`, `reflect`, `swarm`) set their own `subagent_type` for diverse-model review. Respect what the skill prescribes, don't override to `poteto-agent`. +**Use `subagent_type: "poteto-agent"` for any subagent you spawn inside a playbook step** (code-writing delegates, ad-hoc helpers). The chat that receives `/poteto-mode` runs the playbook. Never spawn a `poteto-agent` to run the playbook itself. Routed workflow skills (`how`, `why`, `interrogate`, `reflect`, `swarm`) set their own `subagent_type` for diverse-model review. Respect what the skill prescribes, don't override to `poteto-agent`. **Defaults for every `Task` call.** `run_in_background: true`, agent mode (readonly strips MCP), file pointers not inlined context, explicit model per role (configurable via `/setup-pstack`. Defaults `grok-4.7-xhigh-fast` for code, `claude-opus-5-5-xhigh` for prose and judgment). Code delegates tier by difficulty. The hardest changes (cross-cutting design, gnarly concurrency, subtle algorithms) go to your strongest judgment model (`claude-opus-5-5-xhigh`), whether the task needs judgment on vague intent or is a precisely specified sequence of steps to execute to the letter. Trivial mechanical edits go to your fast code model. Per-role lines in the `/setup-pstack` rule override these defaults and the model choices in the routed skills (`how`, `why`, `arena`, `swarm`, `architect`, `interrogate`, `reflect`). A role with no line keeps its default, and a role line of `inherit-parent` or `auto` runs that role on the parent chat model (omit Task `model`). 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`. +**Match the model to the job.** Before each spawn, start from the role's line or default and pick the reasoning level and speed the job needs. The `/setup-pstack` budget sets each role's starting level, xhigh by default. Raise it one step, up to max, for the hardest or subtlest work. Lower it one step for judgment and two or more steps for routine work. Use fast mode only when the job is urgent, meaning CI or trunk is red or a person is blocked on the result right now. Otherwise use standard speed, the same slug without `-fast`. Verification is the exception and stays on the `swarm workers` model and speed (`grok-4.7-xhigh-fast` by default). Change only the effort level and whether the slug is fast, and keep the family the role names, so a diverse panel stays diverse. If the family has no slug at that level and speed, use its nearest slug. A role on `auto` or `inherit-parent` stays on the parent model. If the Task tool rejects the adjusted slug, do not try another adjusted slug. Use the role's line or default and say so. + You own every subagent's work. Review the diff and write your own summary, don't pass through what it said. A second opinion is the same prompt against a different model. Agreement is high-signal. **Fresh subagents by default.** Give new work to a fresh subagent with consolidated scope, meaning the original brief, every later directive, and the prior agent's report and branch. This holds for a fix round, a follow-up, a retry, and the next queue item. Resume, message, or queue a follow-up on an existing subagent only when the new work strictly needs state that lives in that agent and is costly to move: its local checkout, its uncommitted changes, or a process it still runs, such as a dev server, a simulator, or a babysit watcher. A stop or hold order to a running agent is not reuse. A role such as a PR owner outlives its agent. Once that agent returns, a fresh agent takes the role's next round. Interrupt-chained resumes silently drop directives, so fire a fresh subagent with consolidated scope rather than trusting a "done" summary. diff --git a/pstack/skills/poteto-mode/playbooks/autonomous-run.md b/pstack/skills/poteto-mode/playbooks/autonomous-run.md index 12c3a7d84..e6b88703c 100644 --- a/pstack/skills/poteto-mode/playbooks/autonomous-run.md +++ b/pstack/skills/poteto-mode/playbooks/autonomous-run.md @@ -3,7 +3,7 @@ **You own the exit condition. Define done, then drive to it without stopping.** 1. State the exit condition as a checkable predicate before the first iteration (tests green, repro fixed, all N PRs merged, pixel-diff zero). -2. Pick the wake mechanism using Cursor's `/loop` command (a built-in, not a pstack skill). An event to watch (CI, a merge, a ref advancing) gets a watcher subagent that wakes you on the event, with a long time-based heartbeat as fallback. No event gets a fixed-interval heartbeat sized to when the result is worth re-checking. +2. Pick the wake mechanism using Cursor's `/loop` command (a built-in, not a pstack skill). An event to watch (CI, a merge, a ref advancing) gets a watcher subagent that wakes you on the event, with a long time-based heartbeat as fallback. No event gets a fixed-interval heartbeat sized to when the result is worth re-checking. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. 3. Each iteration makes the smallest change the evidence justifies, verifies it against the predicate, commits if it advanced, discards changes that didn't help. Belt-and-suspenders that "might help" gets reverted, not left to ride. Sequence the work via the **sequence-verifiable-units** principle skill, verifying each unit before the next instead of batching checks at the end. 4. Mid-run discoveries are yours. Address broken skills, related bugs, flaky verifiers, review noise, tooling failures, orphaned follow-ups, and fixable drift yourself via poteto-mode. Put out-of-band fixes in their own PR. Do not park reversible work for the human or use `AskQuestion`. Surface only irreversible actions, genuine product or preference calls no experiment can settle, or a real dead end. Keep the predicate as the main drive, and return to it after each side fix. diff --git a/pstack/skills/poteto-mode/playbooks/autopilot-full.md b/pstack/skills/poteto-mode/playbooks/autopilot-full.md index d5a68c339..b9797cd8a 100644 --- a/pstack/skills/poteto-mode/playbooks/autopilot-full.md +++ b/pstack/skills/poteto-mode/playbooks/autopilot-full.md @@ -1,13 +1,25 @@ ### Autopilot-full -**You own the verdicts, never the PRs. One owner runs each PR from build to merge, and nothing merges without your clean swarm verdict.** For "autopilot this queue", "full autopilot", and one-owner-per-PR programs. Orchestrate runs a standing program whose coordinator lands verified work itself and whose workers never merge. Here each PR's owner carries the whole lifecycle through the merge, and the root keeps only verification, countersigns, and audits. +**You own the verdicts, never the PRs. One owner carries each PR from build to merge, and nothing merges until your swarm, which never wrote the code, has proven the code that lands.** -1. **Mark the operator's items and honor state-then-wait.** Items the operator names stay with the operator. The operator reviews and clicks, and no owner merges one. When the operator asks for the protocol or the plan to be stated, deliver the statement and stop. Execution starts only on the operator's explicit go. -2. **Spawn one owner per PR with the full lifecycle and an early trail.** Resolve the forge once for the program. GitHub CLI (`gh`) is the default. If `command -v origin` succeeds and Origin can resolve the repository, use `origin pr ...` for PR create, edit, view, watch, and merge operations. Otherwise stay on `gh` and record the fallback. Never require Graphite (`gt`). One Cursor cloud agent per PR owns build, the first push, a ready PR, self-proof on the real artifact (the **prove-it-works** principle skill), skeptical Bugbot triage per `../references/bugbot-triage.md`, a slop-strip (the `deslop` skill from the `cursor-team-kit` plugin (`/deslop`)), `/no-comments` (the **no-comments** skill), a rebase onto current trunk, the babysit loop to green (`playbooks/babysit.md`), and the merge itself. Within about 15 minutes, every owner starts a `decisions.tsv` trail per the **show-me-your-work** skill, pushes its first branch snapshot, and opens the PR ready, never draft. After that, the owner pushes its branch again after every verifiable unit (hooks on, a WIP commit is fine). Open the PR before self-proof so the URL, decisions, and checks form a durable trail. Keep `decisions.tsv` uncommitted and return it with the reports. As soon as a subagent starts, the owner adds its ID, expected runtime (at least the longest past run of that kind), and state to a `children.tsv` kept the same way. The owner does the first rebase before the code-ready report and babysit, whether or not trunk has drifted. In fix rounds, the owner keeps that merge base. The owner rebases again only at merge prep (step 5), on a `git merge-tree` conflict with trunk, or on a CI failure that comes from a change on trunk. When the shipped code is final, after the slop-strip and `/no-comments`, it reports the code-ready head SHA. It also reports the SHA of each later push that changes the patch. Self-proof, CI, and babysit then run in parallel with the swarm. The owner reports merge-ready with the head SHA when self-proof, CI, and babysit finish. Before a push that starts a round, run the pre-review checks that the repo's AGENTS.md files and rules name for the touched paths. Run them on the committed head. A hook pass is not proof. To publish each rebase, push the owner's own branch with `git push --force-with-lease` after an `ls-remote` check. Never force-push a shared branch. The merge is the one step an owner may not take alone. Step 4 gates it. -3. **Run owners in true parallel and never stack.** Many owners at once when PRs are self-contained: one writer per branch, disjoint files, cross-PR drift absorbed by rebase. Only genuinely overlapping work serializes. Self-contained PRs branch straight off main, and sequenced work is merge-then-branch. One exception: an owner that must split a genuinely dependent change may hold a short private base-branch stack. -4. **Swarm-verify every round before its merge.** A round starts at the owner's code-ready head SHA and at each later push that changes the PR's patch. At that SHA, fan out parallel independent verifiers per the **swarm** skill and aggregate to one verdict. The merge needs a clean verdict from the round whose patch matches the merge-ready head. Audit the receipts in the merge-ready report before the verdict. The lanes: re-run the gates at that SHA. Prove the load-bearing behavior live on the real surface the change touches (with the matching control skill, such as `control-cli` or `control-ui` from `cursor-team-kit`, or a named driver where none exists). Audit the diff, distrusting the PR body. Run the audit as two or more review lanes with the full brief. Give each lane one main focus, such as consumer parity with trunk, lifetimes and races, or data and config safety. **Regression lane against trunk.** Run the same load-bearing scenario on current trunk. If trunk does not have the feature, record that fact and gate the behavior the diff adds plus the end state the user waits for instead of pretending trunk can produce it. The live lane is the floor, and a verdict without it is not clean. No merge without the root's clean verdict. When the lanes return, send every proven finding against the PR to the owner in one fix-forward. A defect that a lane filed as a note is a finding. For each behavior finding, ask for a red test that covers every site with the same defect. Where no test can show the defect, ask for a repro receipt instead. Add that defect to the next round's review brief. The new head gets a fresh swarm and a fresh verdict, except for lane results that stay valid under the patch-id rule in `playbooks/shipping.md`. -5. **On a clean verdict the owner merges, and a fresh owner takes the next item.** The owner merges only from a head freshly rebased onto trunk. Merge prep never comes before a round's lanes start, and it ends with a rebase onto current trunk right before the merge. After the merge-prep rebase, the owner reports the new head SHA. CI must pass on that head before the merge, and the patch-id rule decides whether the round's verdict still holds. Once that head is green and its patch-id matches the verdict's under the patch-id rule in `playbooks/shipping.md`, a later trunk move does not force another rebase. Right before the merge, fetch trunk and check that `git merge-tree` of the head against current trunk is clean. Also check that no path in `git diff --name-only $(git merge-base HEAD origin/main) origin/main` is a path the PR changes or a path that decides which CI runs for it, such as the repo's CI config paths. If either check fails, rebase again, report the new head SHA, wait for CI to pass on it, and repeat these checks. A new head voids the verdict unless the patch-id is unchanged. The owner squash-merges its own PR through the resolved forge and returns. A fresh owner picks up the next self-contained item from the queue, per poteto-mode's Subagents section. The operator's full-autonomy grant plus the root's clean verdict is the merge authorization that babysitting alone never has. Operator-named items stop at merge-ready and wait for the operator's click. -6. **Run the root layer.** A genuinely new raise of a pinned gate or budget value (a limit CI only lets tighten) needs your fresh countersign, granted only after verifier proof. If the operator's grant or standing orders cover approvals, that countersign is the approval. The owner records it in the form that the tool's approval contract allows, with a pointer to the root's countersign. A lane checks the record against that countersign. The root never gives or bypasses an approval that the forge enforces. Absorbing values that already landed on main is drift, not a raise. Run an audit tick over all owners every hour. On the operator's go, arm `/loop 1h` with a prompt that runs this tick. `/loop` works in local and cloud roots. Never leave the cadence to memory or lossy completion notifications. At each tick, re-read this playbook from trunk with `git show origin/main:pstack/skills/poteto-mode/playbooks/autopilot-full.md` and audit the operation against it. Fix drift during that tick. Probe each owner with a generic liveness or status check, and collect the decision trails. Count only side effects as progress: commits, pushes, PR or check deltas, and store reports. Treat a lane that errors, or that passes its expected runtime without a side effect, as stuck. Stand it down and dispatch a replacement at once. Do not wait for a polite return. The tick judges an owner by the pushed branch and the decision trail that step 2 requires, and it replaces an owner whose agent cannot start a turn. Each tick also runs the lane stuck test over the program's agent list, where the platform has one, and over every owner's `children.tsv`. Whether or not a stop works, the root has the owner record each stuck subagent as stuck and, if its work is still needed, replace it. Each replacement that stalls gets the same steps. The root takes both steps when the owner cannot. A stall never proves or drops the work. When merges batch, run a retro pass and a post-merge bot-comment sweep. End the tick only when no delegated work is left, even after the last merge. -7. **Stand down instantly on the operator's stop.** The operator's hold or stand-down reaches every owner as a zero-writes order immediately. Owners hold their briefs until the operator releases them. +A verdict covers one patch and green CI covers one head. Step 4 runs a full swarm once, and again only for a major change. The patch-id rule in `playbooks/shipping.md` can carry a lane's result, and every new head needs its own green CI. -**Reply:** the queue with each PR's owner, state, and head SHA. Each verdict and the swarm that produced it. What merged and what each fresh owner took next. Countersigns granted and why. Open operator gates. Where the collected decision trails live. +1. **The operator's gates come first.** The grant covers only the queue's items, so items the operator names as theirs and PRs outside the queue stop at merge-ready. A request to state the plan is not a go. +2. **Spawn one owner per PR, and keep its work where a replacement can find it.** Resolve the forge once for the program. GitHub CLI (`gh`) is the default. If `command -v origin` succeeds and Origin can resolve the repository, use `origin pr` for every PR operation. Otherwise stay on `gh` and record the fallback. Never require Graphite (`gt`). Each owner is a Cursor cloud agent that builds, opens its PR per `playbooks/opening-a-pr.md`, proves the change on the real artifact, babysits to green per `playbooks/babysit.md`, and merges once steps 4 and 5 clear it. + + Open the PR early so CI and bots run alongside the work. The code is ready only after the slop-strip (the `deslop` skill from the `cursor-team-kit` plugin (`/deslop`)) and the `/no-comments` pass, so the full swarm runs on finished code. Then rebase onto current trunk and report the code-ready head before self-proof, so verification runs on the base the patch lands on. Report every later head, and merge-ready once self-proof, CI, and babysit are done. Fix rounds stay on that base unless trunk conflicts or breaks CI, because each rebase restarts CI. Run the repo's checks for the touched paths on the committed head before the code-ready push and before a later push that needs a verifier. + + Assume the VM dies. Soon after starting, push and start a decision trail in the root agent's store (path in the system prompt) per the **show-me-your-work** skill, then push after every unit worth keeping. Whoever spawns a subagent records its id, state, and a stuck-after runtime from past runs of its kind in that store, so the root can probe it if the parent dies. Force-push only your own branch, with `--force-with-lease` pinned to the SHA you last pushed or took over, and on any push you did not make, stop and report to the root. +3. **Run owners in parallel off trunk, never stacked,** so each PR lands on its own. +4. **Swarm-verify once.** A full swarm runs once, at the code-ready head. Another full swarm runs only for new behavior, a redesign, or a large new diff. A rebase, a merge-conflict resolution, or a minor fix gets a single verifier at the new head. Fan out independent verifiers at the code-ready SHA per the **swarm** skill. They re-run the gates, prove the load-bearing behavior live on the real surface through its control skill (such as `control-ui` or `control-cli` from `cursor-team-kit`) or a named driver, and audit the diff. The verdict is not clean without the live lane, or before you audit the merge-ready report's receipts, because no lane trusts a claim it has not checked. + + Send every proven finding, whatever a lane labeled it, to the owner in one fix-forward, with a red test across every site with the same defect. A defect trunk shows without the PR is recorded against trunk and does not gate the PR, but one the PR copies or newly reaches is the PR's own. Never push a fix yourself, because each branch has one writer. Replace a stuck owner instead. +5. **The owner merges only what you verified, tested on the trunk it lands on.** Green CI alone never authorizes a merge. Once the swarm's lanes have started, merge prep rebases onto current trunk, reports the new head, and waits for green CI. + + Right before merging, fetch trunk and ask whether its changes can affect the patch, because rebasing for every trunk move never ends. Rebase and wait for CI when the code that would land was never tested, as with a conflict, a file both sides changed, or a change to which checks run for the PR. A lockfile both sides changed needs no rebase when a frozen install passes on the merge result and the packages the PR changes resolve there as on the head, because that install tests the lockfile that would land, and a failing install means the rebase must repair it. When trunk changed a caller of the patch or something the patch depends on, such as code it calls or a dependency version, run the patch's checks on the merge result and merge on a pass. Otherwise merge. If trunk would force a third rebase past merge prep, hold and report to the root, which sequences the colliding work. +6. **Run the root layer.** Anything that loosens a gate, such as a raised budget or an allowlist entry, needs a verifier's proof and then your fresh countersign, because the owner is the one who wants green. Under a grant that covers approvals, that countersign is the approval, and the owner's record of it points at your countersign for a lane to check. A disabled or soft-failed check always comes out, because it stops guarding every case. Never give or bypass an approval the forge enforces, because no countersign covers it. + + On the operator's go, arm `/loop 1h` to audit until no delegated work is left, because long runs drift and agents stall silently. Each tick re-reads this playbook from trunk and fixes drift, such as an unreported head or an unrecorded agent, by having its owner record it, because drift is a gap in the record, not a stall. It probes every agent, down to the leaf lanes, and judges each by its side effects, because status text and summaries can hide a stuck child. An agent is stuck when it errors, cannot start turns, or shows nothing new past its stuck-after runtime, and so is an owner that has neither pushed nor written a trail row by the first tick. One waiting on you or the operator is not stuck, so answer it. Replace a stuck agent at once, without waiting for its stop to land. +7. **Stand down at once on the operator's stop.** A hold reaches every owner as a zero-writes order. Each reports where it stands and keeps its brief until the operator releases it. + +**Reply:** each PR's owner, state, head SHA, and verdict, what merged, countersigns and why, open operator gates, and where the trails live. diff --git a/pstack/skills/poteto-mode/playbooks/autopilot-stack.md b/pstack/skills/poteto-mode/playbooks/autopilot-stack.md index 750a75472..8f9c28a1d 100644 --- a/pstack/skills/poteto-mode/playbooks/autopilot-stack.md +++ b/pstack/skills/poteto-mode/playbooks/autopilot-stack.md @@ -1,15 +1,11 @@ ### Autopilot-stack -**You own the stack, never the landing. Build and verify the queue with full autonomy, then hand the operator one linear base-branch stack to review and land.** The sibling of **Autopilot-full**. +**You own the stack, never the landing. Hand the operator one linear stack of base-chained PRs to review and land.** **Autopilot-full** holds except for how verified work leaves. Each tick re-reads both playbooks from trunk, and this one wins where they differ. -1. **Run the owner loop unchanged.** Resolve the forge once for the program. GitHub CLI (`gh`) is the default. If `command -v origin` succeeds and Origin can resolve the repository, use `origin pr ...` for PR create, edit, view, watch, and merge operations. Otherwise stay on `gh` and record the fallback. Never require Graphite (`gt`). One Cursor cloud agent per PR owns its change end to end: build, first push, a ready PR opened before self-proof, self-proof (gates, CI, receipts), skeptical Bugbot triage per `../references/bugbot-triage.md`, a slop-strip (the `deslop` skill from the `cursor-team-kit` plugin (`/deslop`)), `/no-comments` (the **no-comments** skill), and babysit to green per `playbooks/babysit.md`. Owners parallelize when the work is self-contained. Within about 15 minutes, every owner starts a `decisions.tsv` trail per the **show-me-your-work** skill, pushes its first branch snapshot, and opens the PR ready, never draft. After that, the owner pushes its branch again after every verifiable unit (hooks on, a WIP commit is fine). Keep the trail uncommitted and return it in the report. Owners also keep the `children.tsv` of Autopilot-full step 2. -2. **Audit on a real loop.** The root runs an audit tick every hour. On the operator's go, the root arms `/loop 1h` with a prompt that runs this tick, per Autopilot-full step 6. Never leave the cadence to memory or lossy completion notifications. At each tick, re-read this playbook from trunk with `git show origin/main:pstack/skills/poteto-mode/playbooks/autopilot-stack.md` and audit the operation against it. Fix drift during that tick. Probe each owner with a generic liveness or status check. Count only side effects as progress: commits, pushes, PR or check deltas, and store reports. Treat a lane that passes its expected runtime without a side effect as stuck. Stand it down and dispatch a replacement at once. Do not wait for a polite return. Probe all subagents and end the tick per Autopilot-full step 6. -3. **Hold the operator gates.** State-then-wait, so a request to state the plan is not a go. On the operator's stop, every owner takes an immediate zero-writes hold. -4. **Verify each round.** The owner reports its code-ready head SHA once the shipped code is final, and STACK-READY with the exact head SHA when its loop is green. The root verifies each round per Autopilot-full step 4, with STACK-READY in place of merge-ready. Nothing enters the stack unverified. -5. **Append on a clean verdict, never ship.** No owner merges, arms auto-merge, or closes. A clean verdict appends the PR to the one linear base-branch stack, in verified order or an order the operator specified. -6. **Single writer on topology, parallel writers on builds.** Owners push only their own branches and report the tip, current base, and intended parent. The root is the only topology writer. To append a PR, fetch the intended parent, rebase the child branch onto that exact parent tip, push with `--force-with-lease` only after an `ls-remote` check, and set the PR base to the parent branch. Create it with `origin pr create --status open --base ` or `gh pr create --base ` according to the resolved forge. Retarget an existing PR with `origin pr edit --base ` or `gh pr edit --base `. Only the root PR targets trunk. Never submit or register the chain through `gt`. -7. **Absorb drift at the root, then re-verify what moved.** The root fetches current trunk and rebases the chain from bottom to top. When a rebase surfaces conflicts in an owner's files, that owner fixes its own slice and the root pushes the result. A rebase rewrites every SHA above it and voids verdicts at the old SHAs. Apply the patch-id rule in `playbooks/shipping.md` at each verdict SHA. Anything that is no longer valid goes back through this playbook's step 4 before delivery. Re-run mergeability and CI after every rewritten push even when the patch-id is unchanged. The countersign rule is unchanged from Autopilot-full. A genuinely new pin raises a stop for the root's fresh countersign. Absorbing drift of landed values is not a raise. -8. **Deliver the chain.** The deliverable is one linear chain of verified PRs, reviewable bottom-up in the resolved forge, every link carrying its verifier verdict in the PR body or a comment. The operator reviews and lands it, with their own clicks or by arming merge-when-ready. +1. **Run Autopilot-full's owners, minus the landing.** Owners report STACK-READY in place of merge-ready. They never rebase onto another branch, trunk included, and never retarget, merge, close a PR, or arm auto-merge, because the root owns the chain's topology and the operator owns the landing. When an item depends on another, its owner branches from that item's branch, not from trunk. +2. **Give the topology one writer.** Owners push only their own branches and report each tip and its intended parent. Only you rebase or retarget a link, because two writers on one chain undo each other. Append each clean PR in dependency order, with its base set to its parent's branch (`origin pr edit --base ` or `gh pr edit --base `). Never require `gt`. An owner takes your push as its new base and lease, and on a conflict resolves its own slice for you to push. +3. **Absorb drift at the root.** Until landing starts, apply Autopilot-full step 5's test to each link and rebase the chain bottom-up where it calls for one. Then apply the patch-id rule link by link, since a link's base is its parent. +4. **Deliver the chain, then keep your hands off it.** Each link carries its verdict in its PR. Once landing starts, nobody writes to the chain except the post-merge retarget that `playbooks/shipping.md` sanctions, because a second writer breaks a landing in flight. A mid-landing problem goes through you to the operator, and any fix waits for the operator's go. **Choosing between the autopilots.** Autopilot-full when the PRs are independent and landing authority is granted. Autopilot-stack when the operator wants review before landing, the work is sequenced or coupled, or merge authority is withheld. diff --git a/pstack/skills/poteto-mode/playbooks/bug-fix.md b/pstack/skills/poteto-mode/playbooks/bug-fix.md index eeebc42b7..b5de0bfa6 100644 --- a/pstack/skills/poteto-mode/playbooks/bug-fix.md +++ b/pstack/skills/poteto-mode/playbooks/bug-fix.md @@ -1,6 +1,6 @@ ### Bug fix -**You own this task. Plan, review, verify.** Delegate investigation and the fix to subagents, stay in the lead. +**You own this task. Plan, review, verify.** Delegate investigation and the fix to subagents, stay in the lead. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. Be scientific. Every shipped line traces to runtime evidence. Belt-and-suspenders that "might help" is a hypothesis, not a fix. It does not ship. When evidence refutes a hypothesis, revert what it motivated. The smallest change the evidence justifies ships, nothing more. diff --git a/pstack/skills/poteto-mode/playbooks/eval.md b/pstack/skills/poteto-mode/playbooks/eval.md index 3dae34e2e..768a894e2 100644 --- a/pstack/skills/poteto-mode/playbooks/eval.md +++ b/pstack/skills/poteto-mode/playbooks/eval.md @@ -17,7 +17,7 @@ 1. **Frame.** State what variant is under test and what behavior counts as success. Write the rubric (3-6 concrete criteria) for the judge only. Hold it back from candidates. 2. **Set up sanitized environments.** Per-candidate working dir with the variant in place. Plant any context an organic task would have: a project skeleton, the skills the candidate would naturally read. 3. **Author one organic prompt.** What a user would type. No leakage of what's being measured. -4. **Spawn N parallel candidates** on different models per the **arena** skill's Phase B. Each works in its own sanitized dir. Same prompt to each. +4. **Spawn N parallel candidates** on different models per the **arena** skill's Phase B. Each works in its own sanitized dir. Same prompt to each. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. 5. **Spawn one blinded judge** on a different model family per the **arena** skill's Phase C. Judge sees outputs by sanitized label and the rubric, never a model name. 6. **Verify the chain from transcripts, not self-report.** Read each candidate's local transcript under the active workspace's `agent-transcripts/` directory (the system prompt names this path). Do not glob across `~/.cursor/projects/*/`. That crosses workspace boundaries and reads private chats from unrelated projects. Look at which files each candidate actually opened. Grade chain-following from the files it really read plus the shape of the code, never from the candidate's own claims. 7. **Read every candidate output yourself** end to end. Compare to the judge's verdict. Disagreement means a model is biased or the rubric is ambiguous. Synthesize. diff --git a/pstack/skills/poteto-mode/playbooks/feature.md b/pstack/skills/poteto-mode/playbooks/feature.md index 79a6bbbe0..eb1040cb2 100644 --- a/pstack/skills/poteto-mode/playbooks/feature.md +++ b/pstack/skills/poteto-mode/playbooks/feature.md @@ -9,7 +9,7 @@ - **Independent workstreams.** Disjoint files, services, or layers parallelize. Shared writes serialize. - **Shared mutable state.** Default to splitting the target (the **separate-before-serializing-shared-state** principle skill). Serialize only for real invariants. - **Smallest safe decomposition.** If one worker is best, name why. -4. Delegate code-writing to a subagent using your configured feature model (default `grok-4.7-xhigh-fast`) with a specific scope (file paths, named data shape and its organizing structure per **principle-model-the-domain**, a state machine over scattered booleans, a table/registry over branching, a typed model over repeated shape assumptions, chosen before the delegate writes logic, and success criteria). When the implementation admits multiple valid shapes (error handling, abstraction layer, test structure), delegate via the **arena** skill instead so the runners surface the alternatives and the cross-judge guards the pick. Mandatory: no skip-with-reason escape, and Laziness Protocol does not override it (the gain is review separation, not lines saved). A subagent forbidden to spawn satisfies this by owning the diff directly with the same review separation. No "standing by" reply that waits on a nested agent. Comments per **Comments**. Surgical edits, re-ground against the source for upstream-derived files. Port shared-primitive improvements to all consumers and verify each. Commit liberally. +4. Delegate code-writing to a subagent using your configured feature model (default `grok-4.7-xhigh-fast`) with a specific scope (file paths, named data shape and its organizing structure per **principle-model-the-domain**, a state machine over scattered booleans, a table/registry over branching, a typed model over repeated shape assumptions, chosen before the delegate writes logic, and success criteria). Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. When the implementation admits multiple valid shapes (error handling, abstraction layer, test structure), delegate via the **arena** skill instead so the runners surface the alternatives and the cross-judge guards the pick. Mandatory: no skip-with-reason escape, and Laziness Protocol does not override it (the gain is review separation, not lines saved). A subagent forbidden to spawn satisfies this by owning the diff directly with the same review separation. No "standing by" reply that waits on a nested agent. Comments per **Comments**. Surgical edits, re-ground against the source for upstream-derived files. Port shared-primitive improvements to all consumers and verify each. Commit liberally. 5. Verify on the matching surface. "Inconclusive" or wrong-surface is not a pass. Flag it. 6. Rebase into small, ordered commits. Stack follow-ups. Use the **sequence-verifiable-units** principle skill, building, verifying, and committing each small unit before the next. diff --git a/pstack/skills/poteto-mode/playbooks/hillclimb.md b/pstack/skills/poteto-mode/playbooks/hillclimb.md index c6441b92d..e9cf26790 100644 --- a/pstack/skills/poteto-mode/playbooks/hillclimb.md +++ b/pstack/skills/poteto-mode/playbooks/hillclimb.md @@ -9,7 +9,7 @@ Core discipline: one change, one measurement, keep or revert. Never stack untest 3. Open the decision log via the **show-me-your-work** skill. A `decision.tsv`, one row per attempt: id, hypothesis, change, before, after, delta, tests, verdict (kept or reverted), note. Read it before each attempt. Keep it out of the tree (gitignored). 4. Ground each hypothesis in the architecture model from step 1, so it names a specific mechanism ("defer X off the boot path because it blocks first paint"), not "try memoizing something". For a perf metric, order hypotheses by the performance mantras in step 2 of the Perf issue playbook (`playbooks/perf-issue.md`). Borrow only their order, not that step's stop rule. 5. Loop, one hypothesis per iteration: - - Hand the change to a subagent using your configured hillclimb model (default `grok-4.7-xhigh-fast`) with a tight scope. Supervise and review the diff rather than typing it (the **guard-the-context-window** principle skill). When several independent hypotheses are live, fan them to parallel subagents, each in its own worktree (the **separate-before-serializing-shared-state** principle skill). + - Hand the change to a subagent using your configured hillclimb model (default `grok-4.7-xhigh-fast`) with a tight scope. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. Supervise and review the diff rather than typing it (the **guard-the-context-window** principle skill). When several independent hypotheses are live, fan them to parallel subagents, each in its own worktree (the **separate-before-serializing-shared-state** principle skill). - Measure before and after with the frozen harness, and run the regression gate. - Accept only when the metric moves past noise and the gate stays green. Otherwise revert the change in full. A tweak that "might help" is not kept. - One commit per accepted fix, staging only the files you changed (`git add `, never `-A`). Log the row either way, kept or reverted. diff --git a/pstack/skills/poteto-mode/playbooks/multi-phase-plan.md b/pstack/skills/poteto-mode/playbooks/multi-phase-plan.md index f8ed78a65..27e99947b 100644 --- a/pstack/skills/poteto-mode/playbooks/multi-phase-plan.md +++ b/pstack/skills/poteto-mode/playbooks/multi-phase-plan.md @@ -4,7 +4,7 @@ 1. When the change is one or two files with an obvious approach, skip the plan. Say so and stop. 2. Settle open questions by prototype before you write. Run `playbooks/prototype.md` for each. Keep the branch, the SHA, and the screenshots for Appendix A. Ask the operator only about a product or preference call that no run can settle. Give options (the **never-block-on-the-human** principle skill). -3. Explore in subagents with `subagent_type: "poteto-agent"` and an explicit model per the Subagents section (the **guard-the-context-window** principle skill). Each returns file pointers, conventions, test commands, and entry points. No inlined dumps. +3. Explore in subagents with `subagent_type: "poteto-agent"` and an explicit model per the Subagents section (the **guard-the-context-window** principle skill). Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. Each returns file pointers, conventions, test commands, and entry points. No inlined dumps. 4. Copy the skeleton below into the plan file and fill every placeholder. Unless the operator names a path, write the file under the agent store's `docs/`. Keep every heading and every sub-block in the order shown. One section per PR. One PR is one change with its own evidence (the **sequence-verifiable-units** principle skill). Name the execution playbook in **How to read this**. Pick between `playbooks/autopilot-full.md` and `playbooks/autopilot-stack.md` per the rule at the end of `playbooks/autopilot-stack.md`. A standing program takes `playbooks/orchestrate.md`. 5. Write under `/technical-writing` in full, then `/unslop`. The body is one Diátaxis mode, how-to. Appendices hold explanation and reference. Each heading states the task or the finding. No long dashes. No mid-sentence colons. 6. Run `node pstack/skills/poteto-mode/scripts/check-plan.mjs ` and fix every line it prints (the **encode-lessons-in-structure** principle skill). @@ -62,8 +62,8 @@ Tests alone are not sufficient verification. A PR is verified only when its unit ### Verdict and merge, for every PR -- [ ] At the code-ready head SHA and at each later push that changes the patch, run the swarm per `pstack/skills/swarm/SKILL.md`. One gates lane. The ten live lanes from the PR's **Verify, live** block. The perf lane from its **Verify, perf** block. Two or more audit lanes, each with its own focus, that read the diff and the receipts and distrust the PR body. The root audits the receipts in the merge-ready report before the verdict. -- [ ] Clean only when every lane is `PASS`. Findings go back to the owner, including a defect that a lane filed as a note. A new head gets a fresh swarm and a fresh verdict, except for results that stay valid under the patch-id rule in `playbooks/shipping.md`. +- [ ] A full swarm runs once, at the code-ready head. Another full swarm runs only for new behavior, a redesign, or a large new diff. A rebase, a merge-conflict resolution, or a minor fix gets a single verifier at the new head. Run each full swarm per `pstack/skills/swarm/SKILL.md`. One gates lane. The ten live lanes from the PR's **Verify, live** block. The perf lane from its **Verify, perf** block. Two or more audit lanes, each with its own focus, that read the diff and the receipts and distrust the PR body. The root audits the receipts in the merge-ready report before the verdict. +- [ ] Clean only when every lane is `PASS`. Findings go back to the owner, including a defect that a lane filed as a note. A new head gets a fresh verdict, except for results that stay valid under the patch-id rule in `playbooks/shipping.md`. - [ ] ### Boot recipe, for every live lane diff --git a/pstack/skills/poteto-mode/playbooks/opening-a-pr.md b/pstack/skills/poteto-mode/playbooks/opening-a-pr.md index fcd00527c..d729a7a06 100644 --- a/pstack/skills/poteto-mode/playbooks/opening-a-pr.md +++ b/pstack/skills/poteto-mode/playbooks/opening-a-pr.md @@ -25,7 +25,7 @@ After the sections, attach videos or screenshots when they prove a claim. Full S **Built-in PR tool.** When the run provides a built-in PR tool, create, edit, retarget, and mark ready through it, never through a forge CLI. A CLI-made PR misses what the tool tracks. Use the resolved forge for everything the tool does not cover, and for every PR operation when the run has no such tool. -**Size and stacks.** Prefer five narrow PRs to one large PR. A stack is a base-branch chain. The root PR targets trunk. Each child branch rebases onto its parent's exact tip and its PR targets the parent branch. Without a built-in PR tool, per the resolved forge, create a child with `origin pr create --status open --base ` or `gh pr create --base `, and retarget one with `origin pr edit --base ` or `gh pr edit --base `. Branch from trunk only for independent work. Rebase on trunk before substantial stack work. +**Size and stacks.** Prefer five narrow PRs to one large PR. A stack is a base-branch chain. The root PR targets trunk. Each child branch rebases onto its parent's exact tip and its PR targets the parent branch. As a parent merges, move its child onto trunk per `playbooks/shipping.md` step 4. Without a built-in PR tool, per the resolved forge, create a child with `origin pr create --status open --base ` or `gh pr create --base `, and retarget one with `origin pr edit --base ` or `gh pr edit --base `. Branch from trunk only for independent work. Rebase on trunk before substantial stack work. **Readiness.** Open every PR ready, never as a draft. A built-in PR tool can default to draft, so set `draft: false` on every creation call through it. With Origin, pass `--status open`. With `gh`, omit `--draft`. If a PR still opens as a draft, mark it ready through the PR tool, or run `origin pr ready ` or `gh pr ready ` according to the resolved forge. Run `origin pr view ` or `gh pr view ` before you refer to PR status. diff --git a/pstack/skills/poteto-mode/playbooks/orchestrate.md b/pstack/skills/poteto-mode/playbooks/orchestrate.md index d5b6d0080..4ccb24d76 100644 --- a/pstack/skills/poteto-mode/playbooks/orchestrate.md +++ b/pstack/skills/poteto-mode/playbooks/orchestrate.md @@ -12,7 +12,7 @@ Three rules carry the rest. #### Roles and placement -- **Coordinator (this chat).** Local. Frames, authors briefs, drains the inbox, owns the human report, makes judgment calls. It never authors or edits code. Conflicted merges, restacks, and code changes are always tasks. Mechanically landing a verified unit (fast-forward or clean cherry-pick of a worker's commit, then push) is bookkeeping the coordinator may do itself on repos where local git is cheap. Queueing finished work behind an idle stacker is how a deadline harvests nothing. The loop is agentic end to end. Agents are spawned, resumed, and drained only through the Task tool. State reads and writes go through `scripts/orch/orch.ts` at drain points, one command in and one line out. The CLI never spawns, waits, or wakes anything. +- **Coordinator (this chat).** Local. Frames, authors briefs, drains the inbox, owns the human report, makes judgment calls. It never authors or edits code. Conflicted merges, chain rebases, and code changes are always tasks. Mechanically landing a verified unit (fast-forward or clean cherry-pick of a worker's commit, then push) is bookkeeping the coordinator may do itself on repos where local git is cheap. Queueing finished work behind an idle stacker is how a deadline harvests nothing. The loop is agentic end to end. Agents are spawned, resumed, and drained only through the Task tool. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. State reads and writes go through `scripts/orch/orch.ts` at drain points, one command in and one line out. The CLI never spawns, waits, or wakes anything. - **Sub-coordinator.** Always local, durable, one per track, and only when the program exceeds what one coordinator's drains can manage. A track the coordinator can drain itself needs no middle layer. Each nested layer re-pays a full orientation preamble, and a blocking sub-coordinator hides its children while the parent idles. Owns its track's units and boards, authors its workers' briefs, spawns its own workers and verifiers (nesting works to depth 3, and a nested spawn has the full Task schema including `environment`). Rolls up aggregates at wave boundaries. Never forwards raw child reports. Cap in-flight children at what one drain can process, roughly ten, as a rolling window. Never as blocking batches, which cost the slowest child of every batch. - **Worker / verifier.** Always `environment: "cloud"` unless the task needs this machine: `control-ui` or `control-cli` runtime verification (from `cursor-team-kit`). Reading local transcripts under `agent-transcripts/`. Simulators and local IDE state. Auth that exists only here. Cloud agents cannot read the local store, so their briefs inline what they need or point at repo paths. Prefer fewer, broader workers. One writer per worktree or branch (principle-separate-before-serializing-shared-state). Run a unit's verifier on a different model family from its worker. @@ -43,7 +43,8 @@ CONTEXT pointers to files and PRs; upstream reports pasted in full when thi ACCEPTANCE checkable criteria, one per line VERIFY exact commands or the control-skill path, plus known gotchas TIMEBOX rough cap on runtime; on expiry, return partial findings and stop rather than run on -FORBIDDEN no gt, no rebase, no force-push, no fixes outside scope, plus unit-specific bans +FORBIDDEN no PR retargets or merges, no rebase, no force-push, no fixes outside scope, + plus unit-specific bans REPORT status, branch, head SHA, PRs, verdict, what you actually ran, deviations, suggested follow-ups STANDING @@ -58,7 +59,7 @@ A dependency is a context relay, not just ordering. Undeclared upstream context #### Steps 1. **Frame.** State the done predicate as something countable ("all 126 units merged, each ledger-verified `unit-test-verified` or better"). Quantify scope: units, rough effort, expected stacks, and the wall-clock budget. If one agent could finish inside that budget, stop here and run Autonomous run instead. Collapsing must not depend on another document being present. It means do the work directly in this session, plain workers where they help, verification inline, landing as you go, and none of the store, register, or pilot machinery below. Schedule landing against the budget. By roughly 70% of it, stop spawning and land what is verified. Name the tracks per project. A contested decomposition or one-way door goes through the arena skill before the pilot. Present the framing once. Reversible prep proceeds without waiting. -2. **Install the runtime.** Run `orch init`. Open the trail via the show-me-your-work skill, write the standing orders before any spawn, and seed `frontier.json` from existing PRs with `orch frontier set --repo `. +2. **Install the runtime.** Run `orch init`. Open the trail via the show-me-your-work skill, write the standing orders before any spawn, and seed `frontier.json` from existing PRs with `orch frontier set --repo `, adding `--forge origin` when the forge is Origin. 3. **Pilot.** Push one unit through the whole path: brief, worker, verification, stack entry, ledger row, merge. The pilot exists to falsify the brief template, the verify recipe, and the unit size while that costs one agent instead of fifty. Fix the contract from pilot evidence before any fan-out. Scale the pilot to the unit. On programs of near-identical cheap units, the first unit is the pilot, run as a normal unit with its verify command inline, and fan-out starts the moment it lands. The dedicated pilot pipeline (separate verifier agent, audit gate) is for expensive or novel unit shapes, not for clone-units where a serialized pilot has nothing to falsify. 4. **Scale.** Spawn a rolling window of workers up to the in-flight cap, refilling as children finish. Blocking batches pay the slowest child of every batch. Spawn track sub-coordinators only past the one-drain threshold in Roles. Recompute ready work after each drain. Relay upstream reports into downstream briefs. Keep sibling communication upward only. The sampled brief audit runs alongside the wave it samples and stops the next refill on failure, not the current one. 5. **Drain.** Run the queue discipline below at every drain point. @@ -76,9 +77,9 @@ A dependency is a context relay, not just ordering. Undeclared upstream context #### Stack safety -- The frontier is a computed object, never narrative. Recompute `frontier.json` from `gt` after every merge and stack mutation because GitHub base refs drift mid-restack while gt tracking is authoritative: ordered PR list, branch names, head SHAs, a generation number, the lowest unmerged PR. Resolve it where gt knows the stack, normally the stacker's clone. A checkout whose gt metadata never saw the submits reports no PRs and the command errors rather than guessing. -- Exactly one stacker per stack may run `gt`, serialized within its stack. Record the holder in the standing orders. Restacks run in cloud. A local restack at this scale takes the laptop down. -- Workers never rebase and never run `gt`. Babysitters follow `playbooks/babysit.md`, one per stack, scoped to one immutable frontier generation. They report conflicts to the stacker rather than restacking. +- The frontier is a computed object, never narrative. Recompute `frontier.json` after every merge and stack mutation: ordered PR list, branch names, head SHAs, a generation number, the lowest unmerged PR. The command walks the PR base chain through the checkout's current branch, so chain order and PR states come from the forge. It uses `gh` by default. Pass `--forge origin` when the program resolved the forge to Origin. Head SHAs come from local branch refs, so resolve it from the stacker's clone. A branch with no PR errors rather than guessing. +- Exactly one stacker per stack may mutate topology (chain rebases, force-pushes, base retargets, merges), serialized within its stack. Record the holder in the standing orders. Chain rebases run in cloud. A local rebase at this scale takes the laptop down. +- Workers never rebase and never retarget or merge PRs. Babysitters follow `playbooks/babysit.md`, one per stack, scoped to one immutable frontier generation. They report conflicts to the stacker rather than rebasing. - PR closes and retargets go through the stacker only. Closing a base PR orphans every chain above it. Merges and stack surgery are units with briefs like any other. - One retro watcher follows merged PRs for reverts, post-merge CI breaks, and orphaned follow-ups. @@ -104,7 +105,7 @@ A unit is not done until its output is externalized the moment it lands, never b Reaches the human, batched into the status page rather than per item: irreversible actions (force-push to shared branches, deploys, deletions, closing someone else's PR), genuine product or preference calls no experiment settles, a standing order that contradicts observed reality, a program-level dead end that survived a replan. Park each as a `gates.md` entry before asking, and route work around it. -Never reaches the human: frontier nudges, restack mechanics, retries, CI flake triage, review-thread triage, format fixes, scope the brief already forbids (refuse and continue), and "should I keep going". When in doubt, act and log. +Never reaches the human: frontier nudges, rebase mechanics, retries, CI flake triage, review-thread triage, format fixes, scope the brief already forbids (refuse and continue), and "should I keep going". When in doubt, act and log. Mid-run discoveries fix only what blocks the frontier. Everything else parks in follow-ups. At this fan-out a small scope leak multiplies into PRs nobody asked for. diff --git a/pstack/skills/poteto-mode/playbooks/perf-issue.md b/pstack/skills/poteto-mode/playbooks/perf-issue.md index 405c7a436..46dcc9963 100644 --- a/pstack/skills/poteto-mode/playbooks/perf-issue.md +++ b/pstack/skills/poteto-mode/playbooks/perf-issue.md @@ -14,7 +14,7 @@ 7. Do it cheaper. When an earlier mantra meets the target, stop. -3. Plan the fix from the trace. If it crosses a function boundary, `architect` first. Delegate implementation to a subagent using your configured perf-issue model (default `grok-4.7-xhigh-fast`). Review the diff. Capture a post-fix trace. +3. Plan the fix from the trace. If it crosses a function boundary, `architect` first. Delegate implementation to a subagent using your configured perf-issue model (default `grok-4.7-xhigh-fast`). Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. Review the diff. Capture a post-fix trace. Apply the **sequence-verifiable-units** principle skill, verifying each attempt before trying the next. 4. Parse and compare the artifacts (JSON to sqlite, diff). "Inconclusive" or wrong-surface is not a pass. Flag it. 5. Cite the measurement in the PR. diff --git a/pstack/skills/poteto-mode/playbooks/refactoring.md b/pstack/skills/poteto-mode/playbooks/refactoring.md index df2a863b5..c04b07eeb 100644 --- a/pstack/skills/poteto-mode/playbooks/refactoring.md +++ b/pstack/skills/poteto-mode/playbooks/refactoring.md @@ -8,7 +8,7 @@ If the cleanup reveals a missing feature or a real bug, split it out and ship th 2. Name the structure the code is missing per **principle-model-the-domain**. Boring code stays when the shape is already clear and local. The reshape must delete branches or invalid states, not add indirection. 3. Name the target shape. State what the module layout, types, and call graph should be if built today (**principle-foundational-thinking**, **principle-redesign-from-first-principles**). If the target crosses a function boundary, run the **architect** skill for parallel design exploration of the shape before the move. 4. Subtract before you add. Delete dead code, collapse one-caller wrappers, drop redundant validators, and remove orphan references before introducing the new shape (**principle-subtract-before-you-add**). The smallest change that reaches the target shape ships (**principle-laziness-protocol**). A speculative cleanup that "might help" gets reverted. -5. Move in small behavior-preserving steps, each keeping the pin green. For API reshapes, migrate every caller and delete the old API in the same wave (**principle-migrate-callers-then-delete-legacy-apis**). No compatibility shims, no parallel old-and-new paths. Spot-check every rename against the actual files. Renames silently miss usages in strings, prose, and back-references. Delegate the mechanical edits to a subagent using your configured refactoring model (default `grok-4.7-xhigh-fast`) with a specific scope (file paths, the names being moved, the behavior to hold). +5. Move in small behavior-preserving steps, each keeping the pin green. For API reshapes, migrate every caller and delete the old API in the same wave (**principle-migrate-callers-then-delete-legacy-apis**). No compatibility shims, no parallel old-and-new paths. Spot-check every rename against the actual files. Renames silently miss usages in strings, prose, and back-references. Delegate the mechanical edits to a subagent using your configured refactoring model (default `grok-4.7-xhigh-fast`) with a specific scope (file paths, the names being moved, the behavior to hold). Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. 6. Prove behavior is unchanged on the real artifact, not "it compiles" (**principle-prove-it-works**). For larger reshapes, run an equivalence check: a script that diffs old-vs-new outputs, a recorded baseline replayed against the new code, or a smoke run on the matching surface via the relevant control skill. 7. Confirm the change is worth keeping. The success measure is reduced reader load (**principle-minimize-reader-load**). If the diff does not lower reader load somewhere, revert it. 8. Rebase into small ordered commits. A subtraction commit, then the reshape, then any follow-on cleanup. Shape them with the **sequence-verifiable-units** principle skill, so each behavior-preserving slice stays green before the next. Run **Opening a PR**. diff --git a/pstack/skills/poteto-mode/playbooks/runtime-forensics.md b/pstack/skills/poteto-mode/playbooks/runtime-forensics.md index ba659bfe1..6bf64240d 100644 --- a/pstack/skills/poteto-mode/playbooks/runtime-forensics.md +++ b/pstack/skills/poteto-mode/playbooks/runtime-forensics.md @@ -3,7 +3,7 @@ **You own the diagnosis. Instrument the live process, don't theorize from source.** The deliverable is a cited diagnosis, not a fix. 1. Capture the live signal on the matching surface via the control skill: a CPU profile for a spinning process, a heap snapshot for a leak, a CDP trace for a visual glitch. A real artifact, not a guess. -2. Reduce the artifact to the smoking gun: the function on the hot path, the retainer chain from the leaked object to a GC root, the loop firing without input. Parse large artifacts in a subagent (the **guard-the-context-window** principle skill), keep the reduced finding in the main thread. +2. Reduce the artifact to the smoking gun: the function on the hot path, the retainer chain from the leaked object to a GC root, the loop firing without input. Parse large artifacts in a subagent (the **guard-the-context-window** principle skill), keep the reduced finding in the main thread. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. 3. Prove the mechanism before believing it. Inject instrumentation via CDP eval on the running process, or hotfix the live code without reloading, to confirm the hypothesis cheaply. 4. Map the finding back to source: file, symbol, the line that allocates or schedules. 5. Throughput checkpoint stays one line: `throughput checkpoint: n/a, read-only forensics`. diff --git a/pstack/skills/poteto-mode/playbooks/session-pickup.md b/pstack/skills/poteto-mode/playbooks/session-pickup.md index 9b2ed06e0..876b14338 100644 --- a/pstack/skills/poteto-mode/playbooks/session-pickup.md +++ b/pstack/skills/poteto-mode/playbooks/session-pickup.md @@ -2,7 +2,7 @@ **You own the resume point. Read the prior trail, don't redo it.** -1. Locate the prior trail. A local transcript under the active workspace's `agent-transcripts/` directory (the system prompt names the path. Do not glob across `~/.cursor/projects/*/`, that crosses workspace boundaries and reads private chats from unrelated projects), a cloud-agent URL, or a pushed branch. Read the metadata overview and last messages first, then scan back for the decision points. Parse a long transcript in a subagent and keep the reduced timeline in the main thread (the **principle-guard-the-context-window** skill). +1. Locate the prior trail. A local transcript under the active workspace's `agent-transcripts/` directory (the system prompt names the path. Do not glob across `~/.cursor/projects/*/`, that crosses workspace boundaries and reads private chats from unrelated projects), a cloud-agent URL, or a pushed branch. Read the metadata overview and last messages first, then scan back for the decision points. Parse a long transcript in a subagent and keep the reduced timeline in the main thread (the **principle-guard-the-context-window** skill). Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. 2. Reconstruct operational state. The branch and worktree, what already landed (`git log`, `git diff` against the base), the open todos, the decisions made. The prior trail is authoritative input. Resist the bias to re-derive it. 3. Diff done vs pending. Compare what shipped against what was planned, name the resume point, do not re-run the prior repro or redo completed work. A "let me verify from scratch" pass means you're treating the trail as untrustworthy when it's authoritative. 4. Route the remaining work to the matching playbook and pick the verdict: continue the execution, ship a finished recommendation, ratify or override a prior conclusion, or postmortem a failed run. The pickup playbook ends here. The routed playbook owns the rest. diff --git a/pstack/skills/poteto-mode/playbooks/shipping.md b/pstack/skills/poteto-mode/playbooks/shipping.md index 0f1d41f18..0a0c3cfde 100644 --- a/pstack/skills/poteto-mode/playbooks/shipping.md +++ b/pstack/skills/poteto-mode/playbooks/shipping.md @@ -1,17 +1,12 @@ ### Shipping -**You own what lands. Verify each PR independently, land only the verified run from the root, then keep your hands off the queue.** +**You own what lands. Verify each PR independently, land only the verified run from the root, then keep your hands off the stack.** -This is the half after `playbooks/babysit.md`. +1. **Resolve the forge, then verify each PR independently before it lands.** GitHub CLI (`gh`) is the default. If `command -v origin` succeeds and Origin can resolve the repository, use `origin pr` for every PR operation. Otherwise stay on `gh` and record the fallback. Never require Graphite (`gt`). One verifier per PR, each a Cursor cloud agent, runs the change the way a user would with the matching control skill (such as `control-ui` or `control-cli` from `cursor-team-kit`), parent against head, and posts `PASS`, `PASS+NOTES`, or `FAIL` on that PR. Only a verdict from an agent that did not write the code counts, because an author checks what it meant to build. Green CI and an approving bot review are not verdicts, because neither runs the change as a user would. +2. **Land only the contiguous verified run rooted at the bottom.** Walk up from the lowest unmerged PR and stop at the first one without `PASS` or `PASS+NOTES`. The PRs below it are the run, and the top one is the ceiling. A verified PR above an unverified one waits, because it cannot land without the code below it. One whose branch neither holds nor needs that code can move onto trunk and land on its own. It needs a fresh verdict first, because its old one ran with that code beneath it and trunk does not have it. +3. **Re-check that the verdicts still describe the code.** A verdict covers a patch, and a chain rebase moves every PR above it to a new SHA without failing any check. Compare `git patch-id` at the verdict SHA with the current head before trusting an older verdict. When the patches differ only in tests, docs, or lint config, a lane's result stands if what it ran builds the same at both SHAs apart from noise. Noise is a difference that also shows up between two builds of the verdict SHA, or an embedded commit SHA. Report each kind of noise with its files, so a reader can check the call. The change itself still gets fresh checks and a review, because the old verdict never saw it. A lane with no build output, such as a dev server, cannot show this, so rerun it, and re-verify anything else that drifted. +4. **Land bottom-up, one PR at a time.** Always merge the run's lowest unmerged PR, with `origin pr merge ` or `gh pr merge --squash`. On Origin the plain command also merges every PR below the one you name, and on GitHub a PR whose base is still its parent's branch merges into that branch, not trunk. Read the PR back for the result (`origin pr view ` or `gh pr view --json state,mergedAt,baseRefName`), because a merge command can report success when a merge queue only queued the PR, and a requested auto-merge is not a merge. A landed PR reads merged with trunk as its base, not its parent's branch. Arm `--auto` (Origin merge-when-ready or GitHub auto-merge) only on a PR whose verdict covers its current head, because it lands whatever head is ready. After a merge lands, retarget the PR whose base was the merged branch to trunk (`origin pr edit --base ` or `gh pr edit --base `), move its patch onto the merge result, and push with `--force-with-lease`. The verdict covers the patch, not its commits, so step 3's patch-id rule decides whether the verdict survives. Merge it once its checks pass and its verdict survives. +5. **Leave the stack alone while it lands.** Step 4's move of the next PR is the only change to a branch or base, because a merge lands whatever its branch and base hold at that moment, verdict or not. On GitHub, watch the run with the queued watcher from `playbooks/babysit.md` (`scripts/watch-pr/watch-pr --queued-stack --stack-prs `) under `/loop` until it reports `COMPLETE` at the ceiling. On Origin, re-read the current PR with `origin pr view --checks` each time `origin pr checks --watch` returns, until the ceiling reads merged. A base that moves to trunk when its parent merges is expected, not damage to repair. If a merge stalls, find out why before you change anything. +6. **Stop at the ceiling.** Extending the run takes new verdicts, so it is a new pass through step 1. -1. **Resolve the forge, then verify every PR independently.** GitHub CLI (`gh`) is the default. If `command -v origin` succeeds and Origin can resolve the repository, use `origin pr ...` for PR view, watch, edit, and merge operations. Otherwise stay on `gh` and record the fallback. Never require Graphite (`gt`). One subagent per PR, not batched, each a Cursor cloud agent, each exercising the real surface with the matching control skill (such as `control-ui` or `control-cli` from `cursor-team-kit`) against parent versus head. Each returns `PASS`, `PASS+NOTES` or `FAIL` and posts that verdict on its own PR. Safe means a verdict from an agent that did not write the code. CI green is not a verdict, and an approving bot review is not a verdict. -2. **Land only the contiguous verified run rooted at the bottom.** Walk up from the lowest unmerged PR and stop at the first one without a passing verdict, where both `PASS` and `PASS+NOTES` pass. A verified PR sitting above an unverified one is not landable. Report the ceiling as a PR number and say what breaks the chain. -3. **Re-check that each verdict still describes the patch.** Record the verdict head SHA, base SHA, and stable `git patch-id` of that PR's base-to-head diff. A rebase or base retarget rewrites SHAs and can silently invalidate a verdict without touching a check. Before landing a PR, compare the recorded patch-id with its current base-to-head patch-id. When the two patches differ only in tests, docs, or lint config, build what each lane ran. Build it twice at the verdict SHA and once at the current head. A difference is noise if the two builds at the verdict SHA also show it, or if it is an embedded commit SHA. Judge each difference, not each file, and report each kind of noise with its files. If only noise differs, that lane's result stays valid, and checks and a review of the change run fresh. Do not reuse a lane result from a dev server or from anything else with no build output. Rerun that lane. Re-verify anything else when the patch changed. When it did not, keep the code verdict but re-run mergeability and CI at the current head. Never use matching commit messages or a green check from an older SHA as a substitute. -4. **Prepare only the bottom PR.** Fetch current trunk. Rebase the lowest verified branch onto the exact trunk tip when needed, push it, and retarget only that PR to trunk with `origin pr edit --base ` or `gh pr edit --base `. Re-run step 3 after the push. Do not retarget, arm, or merge descendants yet. -5. **Land one PR at a time.** If the bottom PR is mergeable now, squash it with `origin pr merge --squash` or `gh pr merge --squash`. If requirements are still running and the user asked for merge-when-ready, arm only that PR with `origin pr merge --squash --auto` or `gh pr merge --squash --auto`. Origin's `--auto` is Origin merge-when-ready. GitHub's `--auto` is GitHub auto-merge. Wait for that PR to merge before preparing the next one. -6. **Do not read GitHub `autoMergeRequest` as stack readiness.** At most it says GitHub auto-merge was requested for one GitHub PR. It does not prove Origin merge-when-ready is armed, that a descendant is queued, that a patch verdict is current, or that the contiguous stack is safe. Confirm the active forge's state for the current bottom PR, and say that the state is unknown if the active forge cannot report it. -7. **Recompute after every merge.** Fetch trunk, confirm the merged SHA is present, drop the merged PR from the frozen bottom-to-top list, and inspect the new bottom PR's base, head, checks, and patch-id. A host may retarget a child automatically, but do not assume it did. Repeat steps 3 through 6 for that one PR. Independent work stays outside this chain and ships on its own. -8. **Watch the current frontier until it merges or fails. Do not mutate the queue around it.** With Origin, use `origin pr view --checks --comments` and `origin pr checks --watch`, then re-read the PR until it reports merged or blocked. With GitHub, use `scripts/watch-pr/watch-pr --queued-stack --stack-prs ` only as an event wake and poll `gh pr view --json state,mergedAt,mergeStateStatus,statusCheckRollup,autoMergeRequest` after each wake, ignoring `READY` until `mergedAt` is non-null or `state` is `MERGED`. Only then run step 7. Hard-fail only when `state` is `CLOSED` with no `mergedAt`, a required check concludes `FAILURE` or `CANCELLED` and blocks merge after auto-merge is no longer pending, or `mergeStateStatus` is `UNSTABLE` or `DIRTY` with no auto-merge pending. `BLOCKED` while checks are pending or auto-merge is armed is not failure. Do not use Babysit's queued `WAITING`/`merge-queue` stop condition here. Hold the watch under `/loop` in dynamic mode. Report each merge and the new ceiling. If the queue stalls, diagnose before mutating. -9. **Stop at the ceiling.** When the verified run is merged, report what landed, what the next unverified PR is, and what verifying it would take. Extending the run is a new pass through step 1. - -**Reply:** the verified run and its ceiling, each PR's verdict and who produced it, what you armed and how you confirmed it, what landed, and what the next gap needs. +**Reply:** the verified run and its ceiling, each PR's verdict and who produced it, what landed and how you confirmed it, and what the next gap needs. diff --git a/pstack/skills/poteto-mode/playbooks/trace-forensics.md b/pstack/skills/poteto-mode/playbooks/trace-forensics.md index ee54a2391..bf1b00378 100644 --- a/pstack/skills/poteto-mode/playbooks/trace-forensics.md +++ b/pstack/skills/poteto-mode/playbooks/trace-forensics.md @@ -4,7 +4,7 @@ Distinct from **Runtime forensics**, which instruments the live process. Here the capture already exists. The artifact is a fixed dataset, read it, don't re-run it. Keep tooling generic: a DevTools or trace parser for cpuprofile and `.json.gz`, a text editor for a spindump, your heap tooling for a heapsnapshot. -1. Identify the format and load it with the right tool. Parse large artifacts in a subagent (the **principle-guard-the-context-window** skill) and keep the reduced finding in the main thread. +1. Identify the format and load it with the right tool. Parse large artifacts in a subagent (the **principle-guard-the-context-window** skill) and keep the reduced finding in the main thread. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. 2. Transform the raw artifact into a form you can query. Dump the trace or heap snapshot into sqlite, one row per sample, frame, or node. Reach the queryable shape before you read. 3. Narrow to the cause. Query for the frames that hold the most time and walk the call tree to the hot path. For a leak, follow the retainer chain from the leaked object to a GC root. For a spindump, find the thread stuck on-CPU or blocked and its wait reason. 4. Attribute to source. Map the hot frame to file, symbol, and line via the artifact's own symbols. A frame with no source mapping is not yet a diagnosis. Resolve the symbols, or say plainly the artifact does not carry them. diff --git a/pstack/skills/poteto-mode/playbooks/worktree-cleanup.md b/pstack/skills/poteto-mode/playbooks/worktree-cleanup.md index c767ac09a..45e1af693 100644 --- a/pstack/skills/poteto-mode/playbooks/worktree-cleanup.md +++ b/pstack/skills/poteto-mode/playbooks/worktree-cleanup.md @@ -2,7 +2,7 @@ 1. Snapshot and audit. Record `df -h /`, then run `scripts/worktree-audit.sh [-b ] [repo-path]` (principle-build-the-lever). Without `-b` it checks `MERGED` against `origin/HEAD`, so pass `-b origin/` when PRs target another branch. It reads paths from `git worktree list`, never hand-typed, since a hand-typed `myrepo-worktrees/x` misses one that lives at `.cursor/worktrees/myrepo/x` (principle-encode-lessons-in-structure). It classifies each worktree by size, age, merge state, uncommitted work, PR state, and the newest chat that touched it, then suggests a bucket. The transcript scan is slow, so background it. 2. The bucket is advice, not permission. The pinned and active chats are the real artifact (principle-prove-it-works). Get that set from the user or sidebar and cross-check every candidate. The lever has marked `safe` a worktree the user had pinned, so the pinned set wins. -3. Verify usage before deleting. For every `verify-recent-chat` row, or anything you doubt, fan subagents out to read the transcripts and report whether the chat is pinned or ongoing and which worktrees it touches (principle-guard-the-context-window, transcripts are bulk). A pinned chat spawns arena and repro trees into sibling worktrees via background subagents, and those are in use even when their names never hit the sidebar. +3. Verify usage before deleting. For every `verify-recent-chat` row, or anything you doubt, fan subagents out to read the transcripts and report whether the chat is pinned or ongoing and which worktrees it touches (principle-guard-the-context-window, transcripts are bulk). A pinned chat spawns arena and repro trees into sibling worktrees via background subagents, and those are in use even when their names never hit the sidebar. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section. 4. Pause on irreversible loss. `wip:N` is N tracked uncommitted edits. Show the diff and get a decision first, since removing a clean worktree is recoverable from its branch but uncommitted work is gone. `scratch:N` is untracked throwaway, safe to drop, but name the files. Per Autonomy, clean and merged and not-in-use proceeds. `wip` and in-use pause. 5. Prune the confirmed set. Per path, `git worktree remove --force `. If the dir survives on ignored build artifacts, `rm -rf` it, then `git worktree prune`. Branch refs survive, so no commits are lost. Confirm with `df -h /` and re-list. 6. Simulators and other reclaimers. Simulators are usually the next-biggest win. `xcrun simctl --set testing delete all` (XCTestDevices clones), `xcrun simctl delete unavailable`, and `xcrun simctl runtime list` then `runtime delete ` for old runtimes. More when needed: Xcode `DerivedData` and `iOS DeviceSupport`, `~/Library/Application Support/Cursor` (`state.vscdb.backup`, and `snapshots/roots/` where a `` named for a folder you opened as a workspace balloons), package caches (pnpm, uv, brew, yarn). Clear only caches the user has not said to keep. diff --git a/pstack/skills/poteto-mode/scripts/orch/orch.test.ts b/pstack/skills/poteto-mode/scripts/orch/orch.test.ts index 538775e37..2ef435d7d 100644 --- a/pstack/skills/poteto-mode/scripts/orch/orch.test.ts +++ b/pstack/skills/poteto-mode/scripts/orch/orch.test.ts @@ -16,6 +16,7 @@ import { UserError, openStore, parseVerdict, + type Forge, type OpenStoreOptions, type Store, } from "./store.ts"; @@ -76,6 +77,7 @@ async function makeGitStack(directory: string): Promise<{ readonly mergedSha: string; readonly closedSha: string; readonly openSha: string; + readonly draftSha: string; }> { const repo = join(directory, "repo"); await mkdir(repo); @@ -86,70 +88,154 @@ async function makeGitStack(directory: string): Promise<{ git({ repo, args: ["add", "."] }); git({ repo, args: ["commit", "-m", "main"] }); - const branches = ["stack/merged", "stack/closed", "stack/open"]; + const branches = [ + "stack/merged", + "stack/closed", + "stack/open", + "stack/draft", + ]; for (const [index, branch] of branches.entries()) { git({ repo, args: ["checkout", "-b", branch] }); await writeFile(join(repo, `stack-${index}.txt`), `${branch}\n`); git({ repo, args: ["add", "."] }); git({ repo, args: ["commit", "-m", branch] }); } + git({ repo, args: ["checkout", "stack/open"] }); return { repo, mergedSha: git({ repo, args: ["rev-parse", "stack/merged"] }), closedSha: git({ repo, args: ["rev-parse", "stack/closed"] }), openSha: git({ repo, args: ["rev-parse", "stack/open"] }), + draftSha: git({ repo, args: ["rev-parse", "stack/draft"] }), }; } -async function withFakeGt({ +type FixtureState = "open" | "draft" | "merged" | "closed"; + +const FORGE_FIXTURES: Readonly< + Record< + Forge, + { + readonly fields: string; + readonly row: (pr: { + number: number; + state: FixtureState; + head: string; + base: string; + }) => Record; + } + > +> = { + gh: { + fields: "number,state,headRefName,baseRefName", + row: ({ number, state, head, base }) => ({ + number, + state: state === "draft" ? "OPEN" : state.toUpperCase(), + headRefName: head, + baseRefName: base, + }), + }, + origin: { + fields: "number,status,headRef,baseRef", + row: ({ number, state, head, base }) => ({ + number, + status: state, + headRef: head, + baseRef: base, + }), + }, +}; + +function forgeRows( + forge: Forge, + prs: readonly { + number: number; + state: FixtureState; + head: string; + base: string; + }[] +): string { + return JSON.stringify(prs.map((pr) => FORGE_FIXTURES[forge].row(pr))); +} + +async function withFakeForge({ directory, + forge, operation, - output, + currentRows, }: { directory: string; - operation: (outputPath: string) => Promise; - output: string; + forge: Forge; + operation: () => Promise; + currentRows?: string; }): Promise { const bin = join(directory, "bin"); - const outputPath = join(directory, "gt-output.txt"); - await mkdir(bin); - await writeFile(outputPath, output); - const gt = join(bin, "gt"); + const outputPath = join(directory, "forge-output.txt"); + await mkdir(bin, { recursive: true }); + await writeFile( + outputPath, + `${ + currentRows ?? + forgeRows(forge, [ + { + number: 11, + state: "open", + head: "stack/open", + base: "stack/closed", + }, + ]) + }\n` + ); + const { fields } = FORGE_FIXTURES[forge]; + const list = (filter: string, branch: string): string => + `pr list ${filter} ${branch} --state all --json ${fields}`; + const fake = join(bin, forge); await writeFile( - gt, + fake, `#!/usr/bin/env bash set -euo pipefail if [ "$(pwd -P)" != "${realpathSync(join(directory, "repo"))}" ]; then - printf 'gt ran outside the fixture repo: %s\\n' "$(pwd -P)" >&2 + printf '${forge} ran outside the fixture repo: %s\\n' "$(pwd -P)" >&2 exit 2 fi case "$*" in - "--no-interactive log short --stack --reverse") + "${list("--head", "stack/open")}") cat "${outputPath}" ;; - "--no-interactive info stack/merged") - printf 'stack/merged\\nPR #10 (Merged) merged change\\n' + "${list("--head", "stack/closed")}") + printf '%s\\n' '${forgeRows(forge, [{ number: 13, state: "closed", head: "stack/closed", base: "stack/merged" }])}' + ;; + "${list("--head", "stack/merged")}") + printf '%s\\n' '${forgeRows(forge, [{ number: 10, state: "merged", head: "stack/merged", base: "main" }])}' ;; - "--no-interactive info stack/closed") - printf 'stack/closed\\nPR #13 (Closed) closed change\\n' + "${list("--base", "stack/open")}") + printf '%s\\n' '${forgeRows(forge, [ + { number: 14, state: "draft", head: "stack/draft", base: "stack/open" }, + { + number: 15, + state: "closed", + head: "stack/abandoned", + base: "stack/open", + }, + ])}' ;; - "--no-interactive info stack/open") - printf 'stack/open\\nPR #11 (Needs approvals) open change\\n' + "${list("--base", "stack/draft")}") + printf '[]\\n' ;; *) - printf 'unexpected gt arguments: %s\\n' "$*" >&2 + printf 'unexpected ${forge} arguments: %s\\n' "$*" >&2 exit 2 ;; esac ` ); - await chmod(gt, 0o755); + await chmod(fake, 0o755); const originalPath = process.env.PATH; process.env.PATH = `${bin}:${originalPath ?? ""}`; try { - return await operation(outputPath); + return await operation(); } finally { if (originalPath === undefined) { delete process.env.PATH; @@ -406,90 +492,117 @@ describe("Store", () => { ]); }); - it("resolves the ordered Graphite frontier and validates an optional pin", async () => { - const { directory, store } = await initializedStore(); - const stack = await makeGitStack(directory); - const output = `◯ main -◯ stack/merged -◯ stack/closed -◉ stack/open (current) -`; - - await withFakeGt({ - directory, - output, - operation: async () => { - expect(await store.frontier.set({ repo: stack.repo })).toEqual({ - generation: 1, - prs: [ - { - pr: 10, - branches: "stack/merged", - sha: stack.mergedSha, - state: "MERGED", - }, - { - pr: 13, - branches: "stack/closed", - sha: stack.closedSha, - state: "CLOSED", - }, - { - pr: 11, - branches: "stack/open", - sha: stack.openSha, - state: "OPEN", - }, - ], - lowestUnmerged: 11, - }); - expect( - ( - await store.frontier.set({ + for (const forge of ["gh", "origin"] as const) { + it(`resolves the ordered frontier from the ${forge} base chain and validates an optional pin`, async () => { + const { directory, store } = await initializedStore(); + const stack = await makeGitStack(directory); + const forgeParams = forge === "gh" ? {} : { forge }; + + await withFakeForge({ + directory, + forge, + operation: async () => { + expect( + await store.frontier.set({ repo: stack.repo, ...forgeParams }) + ).toEqual({ + generation: 1, + prs: [ + { + pr: 10, + branches: "stack/merged", + sha: stack.mergedSha, + state: "MERGED", + }, + { + pr: 13, + branches: "stack/closed", + sha: stack.closedSha, + state: "CLOSED", + }, + { + pr: 11, + branches: "stack/open", + sha: stack.openSha, + state: "OPEN", + }, + { + pr: 14, + branches: "stack/draft", + sha: stack.draftSha, + state: "OPEN", + }, + ], + lowestUnmerged: 11, + }); + expect( + ( + await store.frontier.set({ + repo: stack.repo, + ...forgeParams, + prs: [10, 13, 11, 14], + }) + ).generation + ).toBe(2); + expect((await store.frontier.show()).generation).toBe(2); + await expect( + store.frontier.set({ repo: stack.repo, - prs: [10, 13, 11], + ...forgeParams, + prs: [10, 11, 12], }) - ).generation - ).toBe(2); - expect((await store.frontier.show()).generation).toBe(2); - await expect( - store.frontier.set({ - repo: stack.repo, - prs: [10, 11, 12], - }) - ).rejects.toThrow( - "frontier pin mismatch: missing from gt: 12; extra in gt: 13" - ); - await expect( - store.frontier.set({ - repo: stack.repo, - prs: [13, 10, 11], - }) - ).rejects.toThrow( - "frontier pin mismatch: order differs: expected 13,10,11; gt 10,13,11" - ); - await expect( - store.frontier.set({ - repo: stack.repo, - prs: [10, 10], - }) - ).rejects.toThrow("--prs must not contain duplicates"); - }, + ).rejects.toThrow( + "frontier pin mismatch: missing from resolved chain: 12; extra in resolved chain: 13,14" + ); + await expect( + store.frontier.set({ + repo: stack.repo, + ...forgeParams, + prs: [13, 10, 11, 14], + }) + ).rejects.toThrow( + "frontier pin mismatch: order differs: expected 13,10,11,14; resolved 10,13,11,14" + ); + await expect( + store.frontier.set({ + repo: stack.repo, + ...forgeParams, + prs: [10, 10], + }) + ).rejects.toThrow("--prs must not contain duplicates"); + }, + }); }); - }); - it("rejects unparseable Graphite output loudly", async () => { + it(`rejects unparseable ${forge} output loudly`, async () => { + const { directory, store } = await initializedStore(); + const stack = await makeGitStack(directory); + + await withFakeForge({ + directory, + forge, + currentRows: "this line is not forge output", + operation: async () => { + await expect( + store.frontier.set({ repo: stack.repo, forge }) + ).rejects.toThrow( + `${forge} pr list --head stack/open output is not valid JSON` + ); + }, + }); + }); + } + + it("names the missing pull request when a chain branch has none", async () => { const { directory, store } = await initializedStore(); const stack = await makeGitStack(directory); - await withFakeGt({ + await withFakeForge({ directory, - output: "◯ main\nthis line is not Graphite output\n", + forge: "gh", + currentRows: "[]", operation: async () => { - await expect( - store.frontier.set({ repo: stack.repo }) - ).rejects.toThrow( - 'gt log short output has an unparseable line 2: "this line is not Graphite output"' + await expect(store.frontier.set({ repo: stack.repo })).rejects.toThrow( + "branch stack/open has no pull request; open one with gh pr create" ); }, }); @@ -548,12 +661,26 @@ describe("orch CLI", () => { const frontierHelp = runCli(["frontier", "set", "--help"]); expect(frontierHelp.code).toBe(0); expect(frontierHelp.stdout).toContain("--repo "); + expect(frontierHelp.stdout).toContain("--forge "); expect(frontierHelp.stdout).toContain("--prs "); const directory = await makeDirectory(); const invalid = runCli(["--store", directory, "unit", "add", "u1"]); expect(invalid.code).toBe(1); expect(invalid.stderr).toContain("required option '--track '"); + + const badForge = runCli([ + "--store", + directory, + "frontier", + "set", + "--repo", + directory, + "--forge", + "gt", + ]); + expect(badForge.code).toBe(1); + expect(badForge.stderr).toContain("Allowed choices are gh, origin"); }); it("accepts ORCH_STORE and emits complete JSON", async () => { diff --git a/pstack/skills/poteto-mode/scripts/orch/orch.ts b/pstack/skills/poteto-mode/scripts/orch/orch.ts index 219ac17bd..14cb5139a 100755 --- a/pstack/skills/poteto-mode/scripts/orch/orch.ts +++ b/pstack/skills/poteto-mode/scripts/orch/orch.ts @@ -7,6 +7,7 @@ import { openStore, parseVerdict, type Counts, + type Forge, type Frontier, type InboxPointer, type OpenGate, @@ -81,6 +82,7 @@ interface GateResolveOptions { interface FrontierSetOptions { readonly repo?: string; + readonly forge: Forge; readonly prs?: readonly number[]; } @@ -472,15 +474,24 @@ function createProgram(io: Io): Command { const frontier = program .command("frontier") - .description("manage the Graphite stack frontier") + .description("manage the stack frontier") .action(() => requireSubcommand(program)); - leaf(frontier, "set", "discover the Graphite stack and set the frontier") + leaf( + frontier, + "set", + "resolve the pull request base chain and set the frontier" + ) .addOption( new Option( "--repo ", "repository directory (or ORCH_REPO)" ).env("ORCH_REPO") ) + .addOption( + new Option("--forge ", "pull request forge") + .choices(["gh", "origin"]) + .default("gh") + ) .option( "--prs ", "optional expected pull request order pin", @@ -493,6 +504,7 @@ function createProgram(io: Io): Command { (store) => store.frontier.set({ repo: frontierRepo(options), + forge: options.forge, prs: options.prs, }), frontierLine diff --git a/pstack/skills/poteto-mode/scripts/orch/store.ts b/pstack/skills/poteto-mode/scripts/orch/store.ts index 5e6c602fb..417368918 100644 --- a/pstack/skills/poteto-mode/scripts/orch/store.ts +++ b/pstack/skills/poteto-mode/scripts/orch/store.ts @@ -167,6 +167,7 @@ export interface ResolveGateParams { export interface SetFrontierParams { readonly repo: string; + readonly forge?: Forge; readonly prs?: readonly number[]; } @@ -967,168 +968,187 @@ function countLine(value: Counts): string { : entries.map(([name, count]) => `${name}=${count}`).join(", "); } -const OPEN_GT_PR_STATUSES = new Set([ - "Trunk branch locked", - "Changes requested", - "Waiting on PRs in this stack to merge", - "Waiting on downstack merge state", - "Draft", - "Required checks failed", - "Undergoing failure detection", - "Merge queue failed on current head commit", - "Handed off to merge queue...", - "Waiting on downstack", - "Merge conflicts", - "Needs reviewers", - "Needs approvals", - "Needs restack", - "Queued to merge...", - "Ready to merge", - "Ready to merge as stack", - "Rebasing...", - "Waiting on CI...", - "Stale, needs rebase onto trunk", - "Unresolved comments", - "Waiting on required CI", - "Waiting to merge...", -]); - -interface GtPullRequest { - readonly pr: number; - readonly state: FrontierPrState; +export type Forge = "gh" | "origin"; + +interface ForgePullRequestQuery { + readonly stateField: string; + readonly headField: string; + readonly baseField: string; + readonly states: ReadonlyMap; +} + +const FORGE_PULL_REQUEST_QUERIES: Readonly< + Record +> = { + gh: { + stateField: "state", + headField: "headRefName", + baseField: "baseRefName", + states: new Map([ + ["OPEN", "OPEN"], + ["MERGED", "MERGED"], + ["CLOSED", "CLOSED"], + ]), + }, + origin: { + stateField: "status", + headField: "headRef", + baseField: "baseRef", + states: new Map([ + ["open", "OPEN"], + ["draft", "OPEN"], + ["merged", "MERGED"], + ["closed", "CLOSED"], + ]), + }, +}; + +export function parseForge(value: string): Forge { + if (value === "gh" || value === "origin") { + return value; + } + throw new UserError("forge must be gh or origin"); } -interface GtFrontierEntry extends GtPullRequest { - readonly branches: string; +interface ForgePullRequest { + readonly pr: number; + readonly state: FrontierPrState; + readonly branch: string; + readonly base: string; } -function parseGtPullRequest({ - branch, - detail, +function runTool({ + command, + args, + repo, }: { - branch: string; - detail: string; -}): GtPullRequest { - const match = - /^(?:\[origin\] )?PR #([1-9]\d*)(?: \(([^)\r\n]+)\))?(?: .+)?$/.exec( - detail - ); - const pr = Number(match?.[1] ?? 0); - if (match === null || !Number.isSafeInteger(pr)) { + command: string; + args: readonly string[]; + repo: string; +}): string { + try { + return execFileSync(command, args, { + cwd: repo, + encoding: "utf8", + env: process.env, + stdio: ["ignore", "pipe", "pipe"], + }); + } catch (error) { throw new UserError( - `gt info output has an invalid PR row for branch ${branch}: ${detail}` + `${command} ${args.join(" ")} failed: ${errorMessage(error)}` ); } - const status = match[2]; - if (status === "Merged") { - return { pr, state: "MERGED" }; - } - if (status === "Closed") { - return { pr, state: "CLOSED" }; - } - if (status === undefined || OPEN_GT_PR_STATUSES.has(status)) { - return { pr, state: "OPEN" }; - } - throw new UserError( - `gt info output has an unknown PR state for branch ${branch}: ${status}` - ); } -function parseGtBranches(raw: string): readonly string[] { - const branches: string[] = []; - const lines = raw.replace(/\r/g, "").split("\n"); - for (const [index, line] of lines.entries()) { - if (line.length === 0) { - continue; - } - const branchMatch = - /^(?:│ )*[◯◉] +([^\s]+)((?: \([^()\r\n]*\))*)$/.exec(line); - if (branchMatch === null) { - throw new UserError( - `gt log short output has an unparseable line ${index + 1}: ${JSON.stringify(line)}` - ); - } - const branch = branchMatch[1] ?? ""; - if (branches.includes(branch)) { - throw new UserError( - `gt log short output contains duplicate branch ${branch}` - ); - } - branches.push(branch); +function listPullRequests({ + filter, + branch, + forge, + repo, +}: { + filter: "--head" | "--base"; + branch: string; + forge: Forge; + repo: string; +}): readonly ForgePullRequest[] { + const query = FORGE_PULL_REQUEST_QUERIES[forge]; + const command = `${forge} pr list ${filter} ${branch}`; + // Every state is fetched because a merged or closed PR still anchors the + // chain, and a draft counts as OPEN. + const raw = runTool({ + command: forge, + args: [ + "pr", + "list", + filter, + branch, + "--state", + "all", + "--json", + ["number", query.stateField, query.headField, query.baseField].join( + "," + ), + ], + repo, + }); + let value: unknown; + try { + value = JSON.parse(raw); + } catch { + throw new UserError(`${command} output is not valid JSON`); } - const trunk = branches[0]; - if (trunk === undefined) { - throw new UserError("gt log short output did not contain a stack"); + if (!isUnknownArray(value)) { + throw new UserError(`${command} output is not a JSON array`); } - return branches.slice(1); + return value.map((row) => { + const status = isRecord(row) ? row[query.stateField] : undefined; + const state = + typeof status === "string" ? (query.states.get(status) ?? null) : null; + const head = isRecord(row) ? row[query.headField] : undefined; + const base = isRecord(row) ? row[query.baseField] : undefined; + if ( + !isRecord(row) || + typeof row.number !== "number" || + !Number.isSafeInteger(row.number) || + row.number < 1 || + state === null || + typeof head !== "string" || + head.length === 0 || + typeof base !== "string" || + base.length === 0 + ) { + throw new UserError(`${command} output has an invalid PR row`); + } + return { pr: row.number, state, branch: head, base }; + }); } -function graphitePullRequest({ +function branchPullRequest({ branch, + forge, repo, }: { branch: string; + forge: Forge; repo: string; -}): GtPullRequest { - let raw: string; - try { - raw = execFileSync("gt", ["--no-interactive", "info", branch], { - cwd: repo, - encoding: "utf8", - env: { ...process.env, NO_COLOR: "1" }, - stdio: ["ignore", "pipe", "pipe"], - }); - } catch (error) { - throw new UserError( - `gt info ${branch} failed: ${errorMessage(error)}` - ); - } - const rows = raw - .replace(/\r/g, "") - .split("\n") - .filter( - (line) => - line.startsWith("PR #") || line.startsWith("[origin] PR #") - ); - if (rows.length === 0) { +}): ForgePullRequest { + const rows = listPullRequests({ filter: "--head", branch, forge, repo }); + const row = rows[0]; + if (row === undefined) { throw new UserError( - `gt info output branch ${branch} has no pull request; this clone's gt metadata may predate the submit, so resolve the frontier from the stacker's clone or after gt sync` + `branch ${branch} has no pull request; open one with ${forge} pr create, or resolve the frontier from a branch whose chain is fully submitted` ); } if (rows.length > 1) { throw new UserError( - `gt info output contains multiple PRs for branch ${branch}` + `branch ${branch} has multiple pull requests (${rows.map((item) => `#${item.pr}`).join(", ")}); close the stale ones first` ); } - return parseGtPullRequest({ branch, detail: rows[0] ?? "" }); + return row; } -function graphiteFrontier(repo: string): readonly GtFrontierEntry[] { - let raw: string; - try { - raw = execFileSync( - "gt", - ["--no-interactive", "log", "short", "--stack", "--reverse"], - { - cwd: repo, - encoding: "utf8", - env: { ...process.env, NO_COLOR: "1" }, - stdio: ["ignore", "pipe", "pipe"], - } - ); - } catch (error) { - throw new UserError( - `gt log short --stack --reverse failed: ${errorMessage(error)}` - ); - } - const result = parseGtBranches(raw).map((branch) => ({ - branches: branch, - ...graphitePullRequest({ branch, repo }), - })); - if (new Set(result.map((row) => row.pr)).size !== result.length) { - throw new UserError("gt info output contains duplicate pull requests"); - } - return result; +function openChildren({ + branch, + forge, + repo, +}: { + branch: string; + forge: Forge; + repo: string; +}): readonly ForgePullRequest[] { + return listPullRequests({ filter: "--base", branch, forge, repo }).filter( + (row) => row.state === "OPEN" + ); +} + +function runGit({ + args, + repo, +}: { + args: readonly string[]; + repo: string; +}): string { + return runTool({ command: "git", args, repo }).trim(); } function branchSha({ @@ -1138,30 +1158,79 @@ function branchSha({ branch: string; repo: string; }): string { - let raw: string; - try { - raw = execFileSync("git", ["rev-parse", branch], { - cwd: repo, - encoding: "utf8", - env: process.env, - stdio: ["ignore", "pipe", "pipe"], - }); - } catch (error) { - throw new UserError( - `git rev-parse ${branch} failed: ${errorMessage(error)}` - ); - } - const sha = raw.trim(); + const sha = runGit({ args: ["rev-parse", branch], repo }); if (!/^[0-9a-f]{40,64}$/i.test(sha)) { throw new UserError(`git rev-parse ${branch} returned an invalid SHA`); } return sha; } -function resolveFrontier(repo: string): readonly FrontierPr[] { - return graphiteFrontier(repo).map((row) => ({ - ...row, - sha: branchSha({ branch: row.branches, repo }), +function currentBranch(repo: string): string { + return runGit({ args: ["symbolic-ref", "--short", "HEAD"], repo }); +} + +function trunkBranch(repo: string): string { + try { + return runGit({ + args: ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], + repo, + }).replace(/^origin\//, ""); + } catch { + // Init-plus-fetch checkouts and local fixtures lack origin/HEAD. + return "main"; + } +} + +function resolveFrontier(repo: string, forge: Forge): readonly FrontierPr[] { + const trunk = trunkBranch(repo); + const current = currentBranch(repo); + if (current === trunk) { + return []; + } + const seen = new Set([current]); + let bottom = branchPullRequest({ branch: current, forge, repo }); + const chain = [bottom]; + while (bottom.base !== trunk) { + if (seen.has(bottom.base)) { + throw new UserError( + `pull request base chain loops at branch ${bottom.base}` + ); + } + seen.add(bottom.base); + bottom = branchPullRequest({ branch: bottom.base, forge, repo }); + chain.unshift(bottom); + } + let tip = current; + for (;;) { + const children = openChildren({ branch: tip, forge, repo }); + const child = children[0]; + if (child === undefined) { + break; + } + if (children.length > 1) { + throw new UserError( + `stack is not linear: branches ${children.map((row) => row.branch).join(", ")} all target ${tip}` + ); + } + if (seen.has(child.branch)) { + throw new UserError( + `pull request base chain loops at branch ${child.branch}` + ); + } + seen.add(child.branch); + chain.push(child); + tip = child.branch; + } + if (new Set(chain.map((row) => row.pr)).size !== chain.length) { + throw new UserError( + "pull request base chain contains duplicate pull requests" + ); + } + return chain.map((row) => ({ + pr: row.pr, + branches: row.branch, + sha: branchSha({ branch: row.branch, repo }), + state: row.state, })); } @@ -1184,14 +1253,14 @@ function validateFrontierPin({ const extra = actual.filter((pr) => !expectedSet.has(pr)); const drift: string[] = []; if (missing.length > 0) { - drift.push(`missing from gt: ${missing.join(",")}`); + drift.push(`missing from resolved chain: ${missing.join(",")}`); } if (extra.length > 0) { - drift.push(`extra in gt: ${extra.join(",")}`); + drift.push(`extra in resolved chain: ${extra.join(",")}`); } if (missing.length === 0 && extra.length === 0) { drift.push( - `order differs: expected ${expected.join(",")}; gt ${actual.join(",")}` + `order differs: expected ${expected.join(",")}; resolved ${actual.join(",")}` ); } throw new UserError(`frontier pin mismatch: ${drift.join("; ")}`); @@ -1484,6 +1553,7 @@ export function openStore( set: async (params) => { await beginWrite(); const repo = resolve(requiredLine(params.repo, "repo directory")); + const forge = parseForge(params.forge ?? "gh"); const pin = params.prs === undefined ? undefined @@ -1492,7 +1562,7 @@ export function openStore( throw new UserError("--prs must not contain duplicates"); } const old = await readFrontier(store); - const prs = resolveFrontier(repo); + const prs = resolveFrontier(repo, forge); if (pin !== undefined) { validateFrontierPin({ actual: prs.map((row) => row.pr), diff --git a/pstack/skills/poteto-mode/scripts/watch-pr/github.test.ts b/pstack/skills/poteto-mode/scripts/watch-pr/github.test.ts index 4ef39d2a1..54722a940 100644 --- a/pstack/skills/poteto-mode/scripts/watch-pr/github.test.ts +++ b/pstack/skills/poteto-mode/scripts/watch-pr/github.test.ts @@ -5,6 +5,7 @@ import { buildReviewThreads, mapRollupNode, orderStack, + parseFastCheck, parsePullRequest, parseReviewThreadPage, resolveChecks, @@ -121,6 +122,37 @@ describe("rollup node mapping", () => { ).toMatchObject({ kind: "code-review-gate" }); }); + it("classifies a Code Review Gate waiting on an owner approval as the gate", () => { + expect( + mapRollupNode({ + __typename: "CheckRun", + name: "Code Review Gate", + status: "COMPLETED", + conclusion: "ACTION_REQUIRED", + description: "1 of 2 policies", + }) + ).toMatchObject({ + kind: "code-review-gate", + reportedState: "ACTION_REQUIRED", + }); + }); + + it("classifies a fast-path Code Review Gate waiting on an owner approval as the gate", () => { + expect( + parseFastCheck({ + name: "Code Review Gate", + state: "ACTION_REQUIRED", + bucket: "fail", + }) + ).toMatchObject({ + kind: "code-review-gate", + reportedState: "ACTION_REQUIRED", + }); + expect( + parseFastCheck({ name: "ci", state: "ACTION_REQUIRED", bucket: "fail" }) + ).toMatchObject({ kind: "failed", reportedState: "ACTION_REQUIRED" }); + }); + it("maps StatusContext states and drops unknown typenames", () => { expect( mapRollupNode({ diff --git a/pstack/skills/poteto-mode/scripts/watch-pr/github.ts b/pstack/skills/poteto-mode/scripts/watch-pr/github.ts index cfc3764a0..4828b0299 100644 --- a/pstack/skills/poteto-mode/scripts/watch-pr/github.ts +++ b/pstack/skills/poteto-mode/scripts/watch-pr/github.ts @@ -237,22 +237,21 @@ export function parseFastCheck(value: unknown): T.Check { const details = checkDetails(object, "name"); const state = string(object.state, "check.state").toUpperCase(); const bucket = string(object.bucket, "check.bucket"); - if ( - bucket === "fail" || - ["FAILURE", "ERROR", "ACTION_REQUIRED"].includes(state) - ) + if (state === "ACTION_REQUIRED") return gateOr("failed", details, state); + if (bucket === "fail" || ["FAILURE", "ERROR"].includes(state)) return { ...details, kind: "failed", reportedState: state }; - if (bucket === "pending") return pendingOrGate(details, state); + if (bucket === "pending") return gateOr("pending", details, state); if (bucket === "pass") return { ...details, kind: "passed", reportedState: state }; if (bucket === "skipping") return { ...details, kind: "skipped", reportedState: state }; return { ...details, kind: "failed", reportedState: state }; } -// The owner-approval gate is excluded from pending everywhere, so the rule has -// one home. Classifying it as pending on either read path makes the watcher -// wait on a human, which is the behaviour #172004 removed from the Python. -function pendingOrGate( +// Every read path classifies the owner-approval gate here, so the rule has one +// home. A gate that is pending or waits on an owner approval must not count as +// pending or failed, or the watcher waits on a human. +function gateOr( + kind: "pending" | "failed", details: { readonly name: string; readonly description: string; @@ -268,7 +267,7 @@ function pendingOrGate( name: "Code Review Gate", reportedState, } - : { ...details, kind: "pending", reportedState }; + : { ...details, kind, reportedState }; } export function mapRollupNode(value: unknown): T.Check | null { const object = record(value, "rollup node"); @@ -288,22 +287,19 @@ export function mapRollupNode(value: unknown): T.Check | null { ? object.conclusion.toUpperCase() : ""; if (status !== "COMPLETED") - return pendingOrGate({ ...details, link }, "PENDING"); + return gateOr("pending", { ...details, link }, "PENDING"); if (conclusion === "SUCCESS") return { ...details, link, kind: "passed", reportedState: "SUCCESS" }; if (conclusion === "NEUTRAL" || conclusion === "SKIPPED") return { ...details, link, kind: "skipped", reportedState: conclusion }; - return { - ...details, - link, - kind: "failed", - reportedState: conclusion === "ACTION_REQUIRED" ? conclusion : "FAILURE", - }; + if (conclusion === "ACTION_REQUIRED") + return gateOr("failed", { ...details, link }, conclusion); + return { ...details, link, kind: "failed", reportedState: "FAILURE" }; } const state = typeof object.state === "string" ? object.state.toUpperCase() : ""; if (state === "PENDING" || state === "EXPECTED") - return pendingOrGate({ ...details, link }, "PENDING"); + return gateOr("pending", { ...details, link }, "PENDING"); return state === "SUCCESS" ? { ...details, link, kind: "passed", reportedState: state } : { ...details, link, kind: "failed", reportedState: state || "FAILURE" }; diff --git a/pstack/skills/poteto-mode/scripts/worktree-audit.sh b/pstack/skills/poteto-mode/scripts/worktree-audit.sh index b2d119218..743c4e925 100755 --- a/pstack/skills/poteto-mode/scripts/worktree-audit.sh +++ b/pstack/skills/poteto-mode/scripts/worktree-audit.sh @@ -47,10 +47,19 @@ origin/*) git fetch origin "+refs/heads/${base#origin/}:refs/remotes/$base" --qu esac git rev-parse --verify --quiet "$base^{commit}" >/dev/null 2>&1 || { echo "error: base ref not found: $base" >&2; exit 1; } -# PR state by branch, fetched once. Empty if gh is unavailable. +# PR state by branch, fetched once as [{number, state, head}] with a lowercase +# state. Origin when its CLI can read this repo's PRs, otherwise gh. Empty when +# neither can. prs=$(mktemp) -gh pr list --author "@me" --state all --limit 1000 \ - --json number,state,headRefName 2>/dev/null > "$prs" || echo "[]" > "$prs" +if command -v origin >/dev/null 2>&1 \ + && raw=$(origin pr list --author "@me" --state all --limit 1000 \ + --json number,status,headRef 2>/dev/null); then + printf '%s' "$raw" | jq '[.[] | {number, state: .status, head: .headRef}]' > "$prs" 2>/dev/null +elif raw=$(gh pr list --author "@me" --state all --limit 1000 \ + --json number,state,headRefName 2>/dev/null); then + printf '%s' "$raw" | jq '[.[] | {number, state: (.state | ascii_downcase), head: .headRefName}]' > "$prs" 2>/dev/null +fi +[ -s "$prs" ] || echo "[]" > "$prs" # Transcripts dir: ~/.cursor/projects//agent-transcripts. slug=$(printf '%s' "$main_wt" | sed 's#^/##; s#/#-#g') @@ -88,7 +97,7 @@ git worktree list --porcelain | awk '/^worktree /{print $2}' | while read -r wt; else remote=no-remote; fi pr=$([ -n "$branch" ] && jq -r --arg b "$branch" \ - '.[] | select(.headRefName==$b) | "#\(.number)/\(.state)"' "$prs" 2>/dev/null | head -1) + '.[] | select(.head==$b) | "#\(.number)/\(.state)"' "$prs" 2>/dev/null | head -1) [ -z "$pr" ] && pr="-" # Most recent chat whose transcript operated in this worktree. Match path @@ -103,7 +112,7 @@ git worktree list --porcelain | awk '/^worktree /{print $2}' | while read -r wt; recent=$([ "$last_ts" -gt 0 ] 2>/dev/null && [ $(( (now - last_ts) / 86400 )) -le 4 ] && echo yes || echo no) case "$dirty" in wip:*) bucket=hold-wip ;; *) - case "$pr" in *OPEN*) bucket=hold-open-pr ;; *) + case "$pr" in */open|*/draft) bucket=hold-open-pr ;; *) if [ "$recent" = yes ]; then bucket=verify-recent-chat elif [ "$merged" = YES ] || [ "$pr" != "-" ]; then bucket=safe else bucket=review; fi ;; diff --git a/pstack/skills/recall/SKILL.md b/pstack/skills/recall/SKILL.md index a70062a3f..cd8cc2079 100644 --- a/pstack/skills/recall/SKILL.md +++ b/pstack/skills/recall/SKILL.md @@ -16,7 +16,7 @@ Transcripts live at `~/.cursor/projects//agent-transcripts//.j 1. Classify, then route. One specific prior chat to resume is the `session-pickup` playbook, not this. Turning habits into a durable skill is `automate-me`. A human-readable summary of your work is a different task. Recall loads working context across recent chats before you act. If the user already gave you a full state capsule (paths, branch, the change), use it and skip the mining. 2. Lock the scope before searching. Pin the window ("recent" is a real range, default the last 7 days), the topic if named, and the workspace (default the active one. Never read another project's transcripts without being asked). State the scope back. Never quietly turn "all" into "recent N". -3. Fan out across your chat history. Spawn parallel subagents on a fast, cheap model, each taking a slice of the corpus. Tell every subagent to order candidates by real modification time (`ls -t`) and never by UUID name, grep the topic first and then read only the matching chats and only their relevant regions, and skip the current chat plus obvious noise (subagent, eval, and test chats). Each returns the same schema, one block per chat: topic, the user's goal, decisions, open threads, struggles and corrections, and artifacts (PRs, tickets, branches), each citing the chat UUID. For one or two chats, skip the fan-out and search directly. The raw transcripts stay in the subagents. The main thread gets only their findings. +3. Fan out across your chat history. Spawn parallel subagents on a fast, cheap model, each taking a slice of the corpus. Tell every subagent to order candidates by real modification time (`ls -t`) and never by UUID name, grep the topic first and then read only the matching chats and only their relevant regions, and skip the current chat plus obvious noise (subagent, eval, and test chats). Each returns the same schema, one block per chat: topic, the user's goal, decisions, open threads, struggles and corrections, and artifacts (PRs, tickets, branches), each citing the chat UUID. For one or two chats, skip the fan-out and search directly. The raw transcripts stay in the subagents. The main thread gets only their findings. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. 4. Sweep the shared record whenever the topic names a feature, file, subsystem, area, or bug. This is the default, not a judgment call, and "my work on X" does not exempt it. Hand it to the **why** skill's source investigators, but steer their question from "why was this built this way" to "what's the current state, what's been tried and didn't hold, and what are users still reporting". Reuse its per-source playbooks, run the investigators in parallel with the chat-history mining, and inherit its posture: one investigator per source, null results are findings, skip an unavailable MCP and say so. Fold what comes back into the brief. Skip this step only for pure activity recall with no named target ("what did I do this week"), where your own history and live state are the entire answer. 5. Verify against live state. Take the PRs, branches, and tickets that the mining and the sweep surfaced and check them with `git` and `gh`. When the answer hinges on what an agent actually did (the tools it ran, files it read, errors it hit), read the full transcript, not just a trimmed local copy. 6. Write the brief to the contract below. Group by thread. Stay on the named topic. diff --git a/pstack/skills/reflect/SKILL.md b/pstack/skills/reflect/SKILL.md index b6bd41afd..998ae6530 100644 --- a/pstack/skills/reflect/SKILL.md +++ b/pstack/skills/reflect/SKILL.md @@ -24,7 +24,7 @@ That covers the legacy flat (`.jsonl`), current nested (`/.jsonl`), One message, three `Task` calls, `subagent_type: generalPurpose`, agent mode (`readonly: false`). Reviewers need MCP access to look up the tickets, chat threads, and observability traces the transcript references, and readonly strips MCPs. -Each reviewer and the synthesizer name a role line in the `pstack-models.mdc` rule and a default. Set `model` to that line's value, or to the default if the rule or the line is missing. Leave `model` unset when the value is `auto` or `inherit-parent`. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. +Each reviewer and the synthesizer name a role line in the `pstack-models.mdc` rule and a default. Set `model` to that line's value, or to the default if the rule or the line is missing. Leave `model` unset when the value is `auto` or `inherit-parent`. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. | Lens | Role line | Default `model` | Prompt template | |---|---|---|---| diff --git a/pstack/skills/show-me-your-work/SKILL.md b/pstack/skills/show-me-your-work/SKILL.md index d350a150c..ada1bba1e 100644 --- a/pstack/skills/show-me-your-work/SKILL.md +++ b/pstack/skills/show-me-your-work/SKILL.md @@ -64,7 +64,7 @@ Correct the log, not the story. The audit never edits or removes a row, even an ## Cross-model review of the trail -Before handing back, spawn a subagent on a different model family from the one that did the work. Self-review is not a substitute. The subagent reads the audit trail and the run's transcript, then flags what the user should pay attention to. Not a redo of the work, a scan for what's suboptimal or risky. +Before handing back, spawn a subagent on a different model family from the one that did the work. Self-review is not a substitute. The subagent reads the audit trail and the run's transcript, then flags what the user should pay attention to. Not a redo of the work, a scan for what's suboptimal or risky. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. - Decisions logged with weak or absent evidence. - Verification steps skipped or claimed without proof in the transcript. diff --git a/pstack/skills/swarm/SKILL.md b/pstack/skills/swarm/SKILL.md index db22b8787..ed99acb98 100644 --- a/pstack/skills/swarm/SKILL.md +++ b/pstack/skills/swarm/SKILL.md @@ -22,7 +22,7 @@ Open a todolist with one entry per phase before launching anything. 1. State the done predicate and the artifact or report the swarm must return. 2. Choose the shape. Partition into slices, race N workers on identical briefs, or mix both. For a race or mixed shape, declare `first pass`, `rank all`, or `best-of` before spawning. 3. Set N from the user or derive it from the shape. N is total workers, not the cloud concurrency limit. -4. Pick the worker model from the `swarm workers` line in `~/.cursor/rules/pstack-models.mdc`. If the rule or that line is missing, use `grok-4.7-xhigh-fast`. For `auto` or `inherit-parent`, omit `model` so the workers run on the parent model. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. For a model race, name each arm's model up front. +4. Pick the worker model from the `swarm workers` line in `~/.cursor/rules/pstack-models.mdc`. If the rule or that line is missing, use `grok-4.7-xhigh-fast`. For `auto` or `inherit-parent`, omit `model` so the workers run on the parent model. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. For a model race, name each arm's model up front. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. 5. Give each worker its own writable output when it writes. When workers verify or measure commits, each brief names the exact SHAs. A measurement brief also names the method (sample count, what one sample is, order). The worker records both in its result. ## Phase B: Fan out diff --git a/pstack/skills/why/SKILL.md b/pstack/skills/why/SKILL.md index bc2a3eb4f..b5fe93f8b 100644 --- a/pstack/skills/why/SKILL.md +++ b/pstack/skills/why/SKILL.md @@ -8,7 +8,7 @@ disable-model-invocation: true Investigate the motivation and intent behind code. The companion `how` skill answers what the code does and how it works. `why` answers what forces led to its shape. -Each spawn below names a role line in the `pstack-models.mdc` rule and a default. Set `model` to that line's value, or to the default if the rule or the line is missing. Leave `model` unset when the value is `auto` or `inherit-parent`. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. +Each spawn below names a role line in the `pstack-models.mdc` rule and a default. Set `model` to that line's value, or to the default if the rule or the line is missing. Leave `model` unset when the value is `auto` or `inherit-parent`. If the Task tool rejects a slug, use the default and say so. If it rejects the default, use the closest valid slug of the same family from its error message. Pick each subagent's reasoning level and fast or standard speed for the job, per the Subagents section of the **poteto-mode** skill. Every spawn is `generalPurpose` with `readonly: false` (agent mode). **Do not use readonly/Ask mode.** It strips MCP access, which disables the MCP-backed investigators and the synthesizer's citation spot-checks. Investigators still shouldn't write anything. From 8043881e5cbb962f8948d1a1f8f8e28089d67c9a Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 23:39:27 -0700 Subject: [PATCH 11/13] pstack: autopilot loops catch the operator up with /ketchup (#616) Co-authored-by: Cursor Agent --- pstack/skills/poteto-mode/playbooks/autopilot-full.md | 4 ++-- pstack/skills/poteto-mode/playbooks/autopilot-stack.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pstack/skills/poteto-mode/playbooks/autopilot-full.md b/pstack/skills/poteto-mode/playbooks/autopilot-full.md index b9797cd8a..068882379 100644 --- a/pstack/skills/poteto-mode/playbooks/autopilot-full.md +++ b/pstack/skills/poteto-mode/playbooks/autopilot-full.md @@ -19,7 +19,7 @@ A verdict covers one patch and green CI covers one head. Step 4 runs a full swar Right before merging, fetch trunk and ask whether its changes can affect the patch, because rebasing for every trunk move never ends. Rebase and wait for CI when the code that would land was never tested, as with a conflict, a file both sides changed, or a change to which checks run for the PR. A lockfile both sides changed needs no rebase when a frozen install passes on the merge result and the packages the PR changes resolve there as on the head, because that install tests the lockfile that would land, and a failing install means the rebase must repair it. When trunk changed a caller of the patch or something the patch depends on, such as code it calls or a dependency version, run the patch's checks on the merge result and merge on a pass. Otherwise merge. If trunk would force a third rebase past merge prep, hold and report to the root, which sequences the colliding work. 6. **Run the root layer.** Anything that loosens a gate, such as a raised budget or an allowlist entry, needs a verifier's proof and then your fresh countersign, because the owner is the one who wants green. Under a grant that covers approvals, that countersign is the approval, and the owner's record of it points at your countersign for a lane to check. A disabled or soft-failed check always comes out, because it stops guarding every case. Never give or bypass an approval the forge enforces, because no countersign covers it. - On the operator's go, arm `/loop 1h` to audit until no delegated work is left, because long runs drift and agents stall silently. Each tick re-reads this playbook from trunk and fixes drift, such as an unreported head or an unrecorded agent, by having its owner record it, because drift is a gap in the record, not a stall. It probes every agent, down to the leaf lanes, and judges each by its side effects, because status text and summaries can hide a stuck child. An agent is stuck when it errors, cannot start turns, or shows nothing new past its stuck-after runtime, and so is an owner that has neither pushed nor written a trail row by the first tick. One waiting on you or the operator is not stuck, so answer it. Replace a stuck agent at once, without waiting for its stop to land. + On the operator's go, arm `/loop 1h` to audit until no delegated work is left, because long runs drift and agents stall silently. Each tick re-reads this playbook from trunk and fixes drift, such as an unreported head or an unrecorded agent, by having its owner record it, because drift is a gap in the record, not a stall. It probes every agent, down to the leaf lanes, and judges each by its side effects, because status text and summaries can hide a stuck child. An agent is stuck when it errors, cannot start turns, or shows nothing new past its stuck-after runtime, and so is an owner that has neither pushed nor written a trail row by the first tick. One waiting on you or the operator is not stuck, so answer it. Replace a stuck agent at once, without waiting for its stop to land. A tick that changed something for the operator ends by catching them up per the **ketchup** skill, and one that changed nothing stays quiet. 7. **Stand down at once on the operator's stop.** A hold reaches every owner as a zero-writes order. Each reports where it stands and keeps its brief until the operator releases it. -**Reply:** each PR's owner, state, head SHA, and verdict, what merged, countersigns and why, open operator gates, and where the trails live. +**Reply:** catch the operator up per the **ketchup** skill, covering each PR's state and verdict, what merged, countersigns and why, and open operator gates, with links to each PR and the trails. diff --git a/pstack/skills/poteto-mode/playbooks/autopilot-stack.md b/pstack/skills/poteto-mode/playbooks/autopilot-stack.md index 8f9c28a1d..d7bdc289e 100644 --- a/pstack/skills/poteto-mode/playbooks/autopilot-stack.md +++ b/pstack/skills/poteto-mode/playbooks/autopilot-stack.md @@ -9,4 +9,4 @@ **Choosing between the autopilots.** Autopilot-full when the PRs are independent and landing authority is granted. Autopilot-stack when the operator wants review before landing, the work is sequenced or coupled, or merge authority is withheld. -**Reply:** links to the stack root and tip, a one-line verdict summary per link, and anything parked or excluded with the reason. +**Reply:** catch the operator up per the **ketchup** skill, with links to the stack root and tip, a one-line verdict per link, and anything parked or excluded with the reason. From 79afd75d5bd51383fbb526b72f36db03180b8301 Mon Sep 17 00:00:00 2001 From: lauren Date: Tue, 13 Oct 2026 23:49:18 -0700 Subject: [PATCH 12/13] feat(pstack): add /sharpen (#615) * feat(pstack): add /deeper, /higher, and /wider prompt rewrites * refactor(pstack): fold /deeper, /higher, /wider into one /sharpen skill * feat(pstack): sharpen the task in place and call it from poteto-mode * pstack: make /sharpen manual-only and keep it from overspecifying tasks * pstack: tighten /sharpen to the user's real goal and leave their choices open * sharpen: always keep one outside check --------- Co-authored-by: Cursor Agent --- pstack/README.md | 1 + pstack/skills/poteto-help/SKILL.md | 1 + pstack/skills/poteto-help/references/recipes.md | 1 + pstack/skills/sharpen/SKILL.md | 7 +++++++ 4 files changed, 10 insertions(+) create mode 100644 pstack/skills/sharpen/SKILL.md diff --git a/pstack/README.md b/pstack/README.md index 6314039f9..dbbef9bca 100644 --- a/pstack/README.md +++ b/pstack/README.md @@ -114,6 +114,7 @@ to keep [`/poteto-mode`](./skills/poteto-mode/SKILL.md) on across turns, pick it | [`/poteto-mode`](./skills/poteto-mode/SKILL.md) | default entry point for any non-trivial task. | | [`/poteto-help`](./skills/poteto-help/SKILL.md) | you're new to pstack, or unsure which skill, playbook, or principle fits. finds out what you're trying to do, answers that part, and hands you a prompt to type. runs only when you type `/poteto-help`. | | [`/in-your-own-words`](./skills/in-your-own-words/SKILL.md) | you want the agent to play back your goal and the problem before it starts. runs only when you type `/in-your-own-words`. | +| [`/sharpen`](./skills/sharpen/SKILL.md) | you want a task approached the way a top expert would, aimed at what you're really after, without overspecifying it or deciding your choices for you. | | [`/how`](./skills/how/SKILL.md) | you want a walkthrough of how a subsystem works. | | [`/why`](./skills/why/SKILL.md) | you want to know why something was built this way. discovers available MCPs at run time and queries each evidence category in parallel (source control, issue tracker, long-form docs, real-time chat, infra observability, error tracking, analytics warehouse). | | [`/recall`](./skills/recall/SKILL.md) | you're starting or resuming work and want your recent context on a topic rebuilt from your own chat history and the shared record, handed back as a tight current-state brief. | diff --git a/pstack/skills/poteto-help/SKILL.md b/pstack/skills/poteto-help/SKILL.md index cbe18c83c..dc443648e 100644 --- a/pstack/skills/poteto-help/SKILL.md +++ b/pstack/skills/poteto-help/SKILL.md @@ -64,6 +64,7 @@ The default answer is `/poteto-mode`, which runs most of the others when its ste |---|---| | Do any non-trivial task with rigor | [`/poteto-mode`](../poteto-mode/SKILL.md) | | Check that the agent understood their goal and the problem before it starts | [`/in-your-own-words`](../in-your-own-words/SKILL.md) | +| Aim a task higher before starting: more expert, more outcome-focused, not overspecified | [`/sharpen`](../sharpen/SKILL.md) | | Know how code works now, or where new code should live | [`/how`](../how/SKILL.md) | | Know why code is shaped this way, or where a number came from | [`/why`](../why/SKILL.md) | | Understand a change or subsystem, explained plainly | [`/teach`](../teach/SKILL.md) | diff --git a/pstack/skills/poteto-help/references/recipes.md b/pstack/skills/poteto-help/references/recipes.md index a78942c78..d13da3a08 100644 --- a/pstack/skills/poteto-help/references/recipes.md +++ b/pstack/skills/poteto-help/references/recipes.md @@ -6,6 +6,7 @@ Swap in the real paths, skills, and done checks. Informal wording works. - `/poteto-mode read . restate the underlying issue in your own words, in plain english.` - `/in-your-own-words ` +- `/sharpen ` - `/poteto-mode investigate why . give me what we know, what data you used, and your best hypotheses. don't change any code yet.` - `use /how to understand . then use /why to find out why it broke recently.` - `/recall my work on from last week, then read .` diff --git a/pstack/skills/sharpen/SKILL.md b/pstack/skills/sharpen/SKILL.md new file mode 100644 index 000000000..4f9bb4ab5 --- /dev/null +++ b/pstack/skills/sharpen/SKILL.md @@ -0,0 +1,7 @@ +--- +name: sharpen +description: "Sharpen a task before starting: the expert approach, what the user is really after, and the right level of specification. Use for /sharpen." +disable-model-invocation: true +--- + +Before starting, ask how a top expert would approach this task (their raw sources, their real methods, an outside check that the result is right), what the user is really after, and whether the task is over- or underspecified. Loosen constraints that don't serve the goal as readily as you add ones that do, and leave choices that are the user's to make open instead of deciding them. Fold in only what makes the work better, not bigger, but always keep one outside check that the result is right, without narrating the framing, then do the work. Don't hand back a rewritten prompt. From 1e5c90a4b6f249917bd21fe361b3a2a7deb741b8 Mon Sep 17 00:00:00 2001 From: lauren Date: Wed, 14 Oct 2026 00:04:30 -0700 Subject: [PATCH 13/13] chore(pstack): bump to 0.15.24 (#617) Co-authored-by: Cursor Agent --- pstack/.cursor-plugin/plugin.json | 2 +- pstack/plugin.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pstack/.cursor-plugin/plugin.json b/pstack/.cursor-plugin/plugin.json index 0da22406a..26d0f6d02 100644 --- a/pstack/.cursor-plugin/plugin.json +++ b/pstack/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "pstack", "displayName": "pstack", - "version": "0.15.23", + "version": "0.15.24", "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.", "author": { "name": "Lauren Tan" diff --git a/pstack/plugin.json b/pstack/plugin.json index 5394623e2..418387709 100644 --- a/pstack/plugin.json +++ b/pstack/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "pstack", - "version": "0.15.23", + "version": "0.15.24", "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.", "author": { "name": "Lauren Tan"