Skip to content

Commit 3bd2972

Browse files
Merge pull request #820 from corbitsdev/cl-7470-add-ponytail-as-a-default-compact-builder-skill
Add compact Ponytail builder guidance
2 parents 9709c81 + b7665aa commit 3bd2972

11 files changed

Lines changed: 196 additions & 28 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename
1616
### Changed
1717

1818
- ChatGPT Codex uses the Corbits system prompt as Responses `instructions`, without fetching or injecting the official GPT-5 Codex prompt.
19+
- Builder now bakes compact Ponytail guidance with default lite mode and uses a smaller native-runtime skill instead of the broad native-integration and TypeScript bodies by default.
1920

2021
## [0.3.17] - 2026-09-05
2122

‎docs/PLUGINS.md‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,13 @@ shape.
322322
from the lazy listing (those stay loadable via explicit `use_skill` /
323323
`resolveSkillBody`), so the model does not auto-suggest background libraries.
324324
First-party recipes that are not operator slashes remain listed for
325-
`skill_search` / `use_skill` when they only set `user-invocable: false` (`dispatch`,
326-
`git-rebase`, `linear-issue-workflow`, `style`, `philosophy`, `native-integration`, `typescript`,
327-
`opsh`). Background libs such as `git-worktrees` set both flags. Bake-only
328-
bodies such as `idiot-proof` also set both flags (baked into builder and
329-
critic; not a slash and not listed for `use_skill`). The slash
330-
command is a direct user entry
331-
point on top.
325+
`skill_search` / `use_skill` when they only set `user-invocable: false`
326+
(`git-rebase`, `linear-issue-workflow`, `style`, `philosophy`,
327+
`native-integration`, `typescript`, `ponytail`, `opsh`). Background libs
328+
such as `git-worktrees` set both flags. Bake-only bodies such as
329+
`idiot-proof` and `native-runtime` also set both flags (baked into workers;
330+
not a slash and not listed for `use_skill`). The slash command is a direct
331+
user entry point on top.
332332
- **First-party catalog.** `plugins/corbits-skills/` (id `corbits-skills`,
333333
kind `command`, `defaultEnabled: true`) is the bundled skill catalog. Origin
334334
`repo` is auto-trusted. Auto-enable applies only when `origin === "repo"` AND

‎docs/PRODUCT.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ recovery line instead of dumping the file path and parse details.
105105

106106
The TUI has an extensible slash-command framework. Built-ins include `/help` (shortcut + command overlay), `/model` (models-only picker for connected accounts; **Alt+A** or `/connect` adds a provider), `/settings`, `/permissions`, `/plugins`, `/clear`, `/new`, `/mcp` (enable, disable, or remove servers), and `/yolo` (persists as the user-global skip-permissions default; `--dangerously-skip-permissions` still forces this process; secret-guard and authz still apply; `/yolo [on|off|toggle]`, bare `/yolo` toggles), plus a `/<name>` command per available workflow. When a session starts with the persisted default already on, the TUI shows a startup notice ("Permission prompts are disabled by your saved default…") so the silent machine-wide default is never invisible; `corbits exec` prints the equivalent warning to stderr. Plugins can register additional commands.
107107

108-
**Default skills** exist out of the gate as first-party slash **actions**, not director names: `/implement`, `/plan`, `/refactor`, `/review`, `/pull-request-review`, `/create-issue`, `/scribe`, `/interview`, `/ast-grep`. Each one is a how-to playbook — the slash sends the skill body to the primary, which follows the steps. Skills do not assign identity or route the fleet; that stays on director system prompts. `/review` is how to review a branch; `/scribe` is how to maintain PRODUCT / ARCHITECTURE / IMPLEMENTATION; `/implement` is the per-commit review/build/critique loop — it does not steal planning from `/plan`. Substantial Builder work consumes a counsel / `/plan` plan first; tiny parent-DIY stays plan-optional. `/plan` authors an eng change plan (files, AC, non-goals, risks, ordered steps) and does not implement. `/create-issue` remains the tracker command: Linear MCP when available; otherwise it `ask_operator`s for the platform (GitHub etc.) and persists `Preferred issue tracker` in `.corbits/MEMORY.md` (GitHub via `gh issue create`). There is no first-party dispatch skill — Skywalker orchestrates natively. `git-rebase`, `linear-issue-workflow`, `style`, `philosophy`, `native-integration`, `typescript`, and `opsh` stay `use_skill` only (`user-invocable: false`). The less-is-more quality bar (`idiot-proof`) is baked into builder and critic — not a slash and not listed for `use_skill`. Draper and emil are not slashes; they remain closed directors via `spawn_agent(agent=…)`. There is no catch-all worker. Slash names are also available to the model via `skill_search` (descriptions) then `use_skill` (body). Disable the catalog in `/plugins` (`corbits-skills`) if you want them gone.
108+
**Default skills** exist out of the gate as first-party slash **actions**, not director names: `/implement`, `/plan`, `/refactor`, `/review`, `/pull-request-review`, `/create-issue`, `/scribe`, `/interview`, `/ast-grep`. Each one is a how-to playbook — the slash sends the skill body to the primary, which follows the steps. Skills do not assign identity or route the fleet; that stays on director system prompts. `/review` is how to review a branch; `/scribe` is how to maintain PRODUCT / ARCHITECTURE / IMPLEMENTATION; `/implement` is the per-commit review/build/critique loop — it does not steal planning from `/plan`. Substantial Builder work consumes a counsel / `/plan` plan first; tiny parent-DIY stays plan-optional. `/plan` authors an eng change plan (files, AC, non-goals, risks, ordered steps) and does not implement. `/create-issue` remains the tracker command: Linear MCP when available; otherwise it `ask_operator`s for the platform (GitHub etc.) and persists `Preferred issue tracker` in `.corbits/MEMORY.md` (GitHub via `gh issue create`). There is no first-party dispatch skill — Skywalker orchestrates natively. `git-rebase`, `linear-issue-workflow`, `style`, `philosophy`, `native-integration`, `typescript`, `ponytail`, and `opsh` stay `use_skill` only (`user-invocable: false`). Bake-only bars such as `idiot-proof` and `native-runtime` are not slashes and are not listed for `use_skill`. Draper and emil are not slashes; they remain closed directors via `spawn_agent(agent=…)`. There is no catch-all worker. Slash names are also available to the model via `skill_search` (descriptions) then `use_skill` (body). Disable the catalog in `/plugins` (`corbits-skills`) if you want them gone.
109109

110110
Providers are **models-first**: there is no standalone `/login` command. `/model` opens a **models-only list** (Recent, Favorites, then connected provider/model rows) — type-to-filter owns printable keys, so Connect is never a bare letter. **Alt+A** or `/connect` opens a dedicated add-provider selector over every first-class kind (OpenAI dual-path ChatGPT OAuth or API key, xAI, OpenCode Zen, Anthropic, Google, OpenCode Go, Z.AI Coding Plan, Ollama, Custom), each annotated with its live account count and never filtered out for “already connected.” **Alt+F** toggles favorite on the highlighted model. **Alt+D** persists the highlighted pair as the default without switching the live session. Advanced provider drill-down (edit/delete/tiers) stays on the advanced surface, not a bare printable key while the model list is filtering. OAuth providers open their existing browser login with a named account step so multiple accounts per kind coexist (`codex/work`, …). API-key providers use the same named-instance step before the key (auth-only form: instance name + key + fixed catalog base URL), so personal and team keys land as distinct catalog rows (`openai/default`, `anthropic/work`, …); reusing a name re-keys that instance after confirm. Custom remains a free-form single endpoint (full manual form). Successful connect refreshes the catalog and reopens the model list focused on the new account’s default model. OpenCode Go lists models from the live `/zen/go/v1/models` catalog (packaged seed on fetch failure), routes each by its protocol metadata (chat completions, OpenAI responses, or Anthropic messages) and can show subscription usage in the status bar when active (rolling 5h / weekly / monthly windows when the usage API responds; omitted on auth or network failure). When Go returns a quota or rate-limit error — including some HTTP 400 responses that carry limit payloads — Corbits classifies them so quota aborts cleanly and short provider rate limits remain retryable. On a free-tier or subscription quota hit, wait for the window to reset or use OpenCode Zen free models.
111111

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
name: native-runtime
3+
user-invocable: false
4+
disable-model-invocation: true
5+
description: Compact Corbits worker runtime invariants for baked prompts.
6+
---
7+
8+
Use Corbits tool names: `read_file`, `write_file`, `edit_file`, `delete_file`,
9+
`grep`, `search_files`, `run_shell`, `web_search`, `web_fetch`,
10+
`manage_tasks`, and `ask_director` for worker questions.
11+
12+
Use file tools for file reads, edits, writes, and deletions. Never use shell
13+
redirects, heredocs, `echo`, `cat`, stream editors, or remove commands as
14+
substitutes for file tools. Use bounded `grep` and `search_files` instead of
15+
unbounded recursive shell searches. Use web tools for URLs; never use curl or
16+
wget.
17+
18+
Workers ask the spawning director with `ask_director`; they cannot reach the
19+
operator. If permission denies an action, make the best effort that remains and
20+
report the assumption or blocker.
21+
22+
Before implementation reports, run the repository-defined typecheck, relevant
23+
tests, and full verification gate when present. Report every exact command with
24+
outcome and exit status.
25+
26+
Finish worker turns with the required `Summary`, `Findings`, `Blockers`, and
27+
`Paths` envelope. Do not leave implied work outside the report.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
name: ponytail
3+
user-invocable: false
4+
description: Compact builder mode guidance for minimal safe implementation diffs.
5+
---
6+
7+
Ponytail is a Builder discipline for keeping implementation diffs small without
8+
weakening the brief.
9+
10+
Default to `lite`: prefer the shortest clear change, reuse existing helpers and
11+
tests, avoid drive-by refactors, and report only decisions or trade-offs the
12+
parent needs. Reporting only decisions and trade-offs never licenses omitting
13+
verification evidence: the Build Gate command-evidence requirement applies in
14+
full in every mode. `off` means ignore Ponytail and follow the rest of the brief
15+
normally. `full` means actively prune scope, split unrelated work into Blockers,
16+
and keep every edit tied to a success criterion. `ultra` means the same
17+
discipline under stricter pressure: delete dead paths you touch, reject
18+
ornamental structure, and stop as soon as acceptance criteria and required
19+
verification are complete.
20+
21+
Escalation ladder: start at `lite`; move to `full` when the brief asks for
22+
minimal surface area, context is expensive, or the diff starts spreading; move
23+
to `ultra` only when requested or when the parent explicitly prioritizes the
24+
smallest viable implementation. De-escalate or turn `off` when Ponytail would
25+
hide necessary reasoning.
26+
27+
Safety precedence is absolute: correctness, validation, security,
28+
accessibility, data integrity, tests, repo conventions, operator requirements,
29+
and explicit success criteria outrank minimal LOC. A mode never weakens those
30+
constraints.
31+
32+
For review or audit, treat Ponytail as a lens for critic, neckbeard, or primary
33+
instructions; do not create a Ponytail director or agent.

‎src/agent/directors/bake-skills.test.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,30 @@ const philosophyOnDisk = stripFrontmatter(
2222
"utf8",
2323
),
2424
);
25+
const ponytailOnDisk = stripFrontmatter(
26+
readFileSync(
27+
join(import.meta.dirname, "../../../plugins/corbits-skills/skills/ponytail/SKILL.md"),
28+
"utf8",
29+
),
30+
);
31+
const nativeRuntimeOnDisk = stripFrontmatter(
32+
readFileSync(
33+
join(import.meta.dirname, "../../../plugins/corbits-skills/skills/native-runtime/SKILL.md"),
34+
"utf8",
35+
),
36+
);
2537

2638
describe("loadBakedSkillBody", () => {
2739
test("returns first-party style and philosophy bodies matching SKILL.md", () => {
2840
expect(loadBakedSkillBody("style")).toBe(styleOnDisk);
2941
expect(loadBakedSkillBody("philosophy")).toBe(philosophyOnDisk);
3042
});
3143

44+
test("returns first-party ponytail and native-runtime bodies matching SKILL.md", () => {
45+
expect(loadBakedSkillBody("ponytail")).toBe(ponytailOnDisk);
46+
expect(loadBakedSkillBody("native-runtime")).toBe(nativeRuntimeOnDisk);
47+
});
48+
3249
test("returns undefined for unknown skill names", () => {
3350
expect(loadBakedSkillBody("does-not-exist-xyz")).toBeUndefined();
3451
});
@@ -45,6 +62,15 @@ describe("formatBakedOptionalSkills", () => {
4562
expect(text).toContain("use_skill is not mounted on workers");
4663
});
4764

65+
test("formats ponytail and native-runtime under Baked skill guidance", () => {
66+
const text = formatBakedOptionalSkills(["ponytail", "native-runtime"]);
67+
expect(text).toContain("# Baked skill guidance");
68+
expect(text).toContain("### ponytail");
69+
expect(text).toContain("### native-runtime");
70+
expect(text).toContain(ponytailOnDisk);
71+
expect(text).toContain(nativeRuntimeOnDisk);
72+
});
73+
4874
test("skips missing names without inventing content", () => {
4975
const text = formatBakedOptionalSkills(["does-not-exist-xyz"]);
5076
expect(text).toBe("");

‎src/agent/directors/builder/package.test.ts‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,16 @@ describe("builderPackage", () => {
6060
expect(p).toContain("`/implement` does not steal planning from `/plan`");
6161
});
6262

63-
test("systemPrompt requires style, philosophy, and idiot-proof prerequisites", () => {
63+
test("systemPrompt requires baked core constraints and Ponytail prerequisites", () => {
6464
const p = builderPackage.systemPrompt;
6565
expect(p).toContain("Prerequisites");
66-
expect(p).toMatch(/style, philosophy, native-integration, and idiot-proof/i);
66+
expect(p).toMatch(/style, philosophy, native-runtime, idiot-proof, and Ponytail/i);
6767
expect(p).toMatch(/use_skill is not mounted/i);
68+
expect(p).toMatch(
69+
/including their TypeScript conventions when TypeScript is the task surface/i,
70+
);
71+
expect(p).not.toMatch(/native-integration, and idiot-proof/i);
72+
expect(p).not.toMatch(/Apply typescript when writing TypeScript/i);
6873
});
6974

7075
test("systemPrompt is implement leaf only (no orchestrate / spawn / review-as-primary)", () => {
@@ -112,13 +117,13 @@ describe("builderPackage", () => {
112117
expect(builderPackage.modelRole).toBe("implement");
113118
});
114119

115-
test("optionalSkills order is style, philosophy, native-integration, idiot-proof, typescript", () => {
120+
test("optionalSkills order is style, philosophy, native-runtime, idiot-proof, ponytail", () => {
116121
expect(builderPackage.optionalSkills).toEqual([
117122
"style",
118123
"philosophy",
119-
"native-integration",
124+
"native-runtime",
120125
"idiot-proof",
121-
"typescript",
126+
"ponytail",
122127
]);
123128
});
124129

‎src/agent/directors/builder/package.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const builderPackage: DirectorPackage = {
1818
"orchestrating or spawning other agents",
1919
],
2020
description: "Implementation worker — edit, verify, report",
21-
optionalSkills: ["style", "philosophy", "native-integration", "idiot-proof", "typescript"],
21+
optionalSkills: ["style", "philosophy", "native-runtime", "idiot-proof", "ponytail"],
2222
tools: { allow: BUILD_TOOLS },
2323
spawn: { maySpawn: false },
2424
tier: "leaf",
@@ -30,7 +30,7 @@ You are a disciplined implementer worker (maySpawn:false) — not Critic, not Ex
3030
3131
## Prerequisites
3232
33-
Before substantial repo work: follow style, philosophy, native-integration, and idiot-proof (baked; use_skill is not mounted). Follow AGENTS.md and /docs. Apply typescript when writing TypeScript.
33+
Before substantial repo work: follow style, philosophy, native-runtime, idiot-proof, and Ponytail (baked; use_skill is not mounted). Follow AGENTS.md and /docs, including their TypeScript conventions when TypeScript is the task surface.
3434
3535
## Plan
3636

‎src/agent/directors/identity.test.ts‎

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe("formatDirectorSystemPrompt", () => {
2121
expect(text.startsWith("Identity: agent id `builder`")).toBe(true);
2222
expect(text).toContain('spawn_agent(agent="builder")');
2323
expect(text).toContain("Model role: implement.");
24-
expect(text).toContain("style, philosophy, native-integration, idiot-proof, typescript");
24+
expect(text).toContain("style, philosophy, native-runtime, idiot-proof, ponytail");
2525
expect(text).toContain(DIRECTOR_REGISTRY.builder.systemPrompt);
2626
});
2727

@@ -30,7 +30,7 @@ describe("formatDirectorSystemPrompt", () => {
3030
expect(text).toContain("Optional skills: none by default");
3131
});
3232

33-
test("bakes real style/philosophy/native-integration/idiot-proof/typescript bodies for builder workers (CL-6803)", () => {
33+
test("bakes real compact Builder skill bodies without broad native-integration or typescript", () => {
3434
const text = formatDirectorSystemPrompt(DIRECTOR_REGISTRY.builder);
3535
const style = stripFrontmatter(
3636
readFileSync(
@@ -44,12 +44,9 @@ describe("formatDirectorSystemPrompt", () => {
4444
"utf8",
4545
),
4646
);
47-
const nativeIntegration = stripFrontmatter(
47+
const nativeRuntime = stripFrontmatter(
4848
readFileSync(
49-
join(
50-
import.meta.dirname,
51-
"../../../plugins/corbits-skills/skills/native-integration/SKILL.md",
52-
),
49+
join(import.meta.dirname, "../../../plugins/corbits-skills/skills/native-runtime/SKILL.md"),
5350
"utf8",
5451
),
5552
);
@@ -59,18 +56,48 @@ describe("formatDirectorSystemPrompt", () => {
5956
"utf8",
6057
),
6158
);
59+
const ponytail = stripFrontmatter(
60+
readFileSync(
61+
join(import.meta.dirname, "../../../plugins/corbits-skills/skills/ponytail/SKILL.md"),
62+
"utf8",
63+
),
64+
);
65+
const nativeIntegration = stripFrontmatter(
66+
readFileSync(
67+
join(
68+
import.meta.dirname,
69+
"../../../plugins/corbits-skills/skills/native-integration/SKILL.md",
70+
),
71+
"utf8",
72+
),
73+
);
6274
const typescript = stripFrontmatter(
6375
readFileSync(
6476
join(import.meta.dirname, "../../../plugins/corbits-skills/skills/typescript/SKILL.md"),
6577
"utf8",
6678
),
6779
);
6880
expect(text).toContain("# Baked skill guidance");
81+
expect(text).toContain("### ponytail");
82+
expect(text).toContain("### native-runtime");
6983
expect(text).toContain(style);
7084
expect(text).toContain(philosophy);
71-
expect(text).toContain(nativeIntegration);
85+
expect(text).toContain(nativeRuntime);
7286
expect(text).toContain(idiotProof);
73-
expect(text).toContain(typescript);
87+
expect(text).toContain(ponytail);
88+
expect(text).toContain("Default to `lite`");
89+
expect(text).not.toContain(nativeIntegration);
90+
expect(text).not.toContain(typescript);
91+
expect(text).not.toContain("### native-integration");
92+
expect(text).not.toContain("### typescript");
93+
});
94+
95+
test("skywalker does not bake Ponytail", () => {
96+
const text = formatDirectorSystemPrompt(DIRECTOR_REGISTRY.skywalker);
97+
expect(text).not.toContain("### ponytail");
98+
expect(text).not.toMatch(/ponytail/i);
99+
expect(text).not.toContain("Default to `lite`");
100+
expect(text).not.toContain("Escalation ladder");
74101
});
75102

76103
test("does not bake skill bodies when optionalSkills is empty", () => {

‎src/agent/directors/skywalker/package.test.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ describe("skywalkerPackage", () => {
6666
]);
6767
});
6868

69+
test("systemPrompt has no Ponytail routing or mode internals", () => {
70+
const p = skywalkerPackage.systemPrompt;
71+
expect(p).not.toMatch(/ponytail/i);
72+
expect(p).not.toContain("Default to `lite`");
73+
expect(p).not.toContain("Escalation ladder");
74+
});
75+
6976
test("primaryIntent and outOfLane", () => {
7077
expect(skywalkerPackage.primaryIntent).toBe(
7178
"Orchestrate; DIY tiny/bounded product edits; spawn for substantial work",

0 commit comments

Comments
 (0)