Add Second opinion, Ship and Founder builtin skills (#196)
* Add Second opinion and Founder builtin skills Second opinion has a fresh reviewer approve the plan and inspect the build, so whoever writes never approves their own work. Founder tests a business idea with simulated roles and a crowd of 100 simulated buyers and ends in a business plan card with a verdict. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ * Add Ship builtin skill for phased software work Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tgvrj34NMnaqvtgvruYQZZ --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3469165c92
commit
4585d3b0ec
@ -55,15 +55,19 @@ function setup(rows: ReturnType<typeof savedSkill>[] = []) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Only the entries competing for the name "Interrogate"; other builtins are irrelevant here. */
|
||||||
|
const interrogate = <T extends { name: string }>(skills: T[]) =>
|
||||||
|
skills.filter((skill) => skill.name.trim().toLowerCase() === "interrogate");
|
||||||
|
|
||||||
describe("built-in skill precedence in the API", () => {
|
describe("built-in skill precedence in the API", () => {
|
||||||
it.each(["interrogate", " Interrogate "])(
|
it.each(["interrogate", " Interrogate "])(
|
||||||
"keeps %j listed, readable and mutable",
|
"keeps %j listed, readable and mutable",
|
||||||
async (name) => {
|
async (name) => {
|
||||||
const { service } = setup([savedSkill(name)]);
|
const { service } = setup([savedSkill(name)]);
|
||||||
await expect(service.list(actor)).resolves.toEqual([
|
expect(interrogate(await service.list(actor))).toEqual([
|
||||||
expect.objectContaining({ id: "saved-1", name, readOnly: false }),
|
expect.objectContaining({ id: "saved-1", name, readOnly: false }),
|
||||||
]);
|
]);
|
||||||
await expect(service.listWithContent(actor)).resolves.toEqual([
|
expect(interrogate(await service.listWithContent(actor))).toEqual([
|
||||||
expect.objectContaining({ id: "saved-1", content: expect.stringContaining("Saved steps") }),
|
expect.objectContaining({ id: "saved-1", content: expect.stringContaining("Saved steps") }),
|
||||||
]);
|
]);
|
||||||
await expect(service.get(actor, { name: " INTERROGATE " })).resolves.toMatchObject({
|
await expect(service.get(actor, { name: " INTERROGATE " })).resolves.toMatchObject({
|
||||||
@ -105,7 +109,7 @@ describe("built-in skill precedence in the API", () => {
|
|||||||
id: "builtin:Interrogate",
|
id: "builtin:Interrogate",
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
});
|
});
|
||||||
await expect(service.list(actor)).resolves.toEqual([
|
expect(interrogate(await service.list(actor))).toEqual([
|
||||||
expect.objectContaining({ id: "builtin:Interrogate" }),
|
expect.objectContaining({ id: "builtin:Interrogate" }),
|
||||||
]);
|
]);
|
||||||
await expect(service.get(actor, { skillId: "saved-1" })).rejects.toThrow();
|
await expect(service.get(actor, { skillId: "saved-1" })).rejects.toThrow();
|
||||||
|
|||||||
@ -21,6 +21,50 @@ const SKILLS: Array<{ name: string; description: string; body: string }> = [
|
|||||||
3. Verify before accusing: for each suspected issue, re-read the code and construct the concrete input or state that triggers the failure. Drop anything you cannot substantiate.
|
3. Verify before accusing: for each suspected issue, re-read the code and construct the concrete input or state that triggers the failure. Drop anything you cannot substantiate.
|
||||||
4. Synthesize a verdict: ship, ship after fixes, or do not ship. List the confirmed findings ordered by severity, each with its location and failure scenario, then any open questions. If nothing survived verification, say so plainly instead of inventing nitpicks.`,
|
4. Synthesize a verdict: ship, ship after fixes, or do not ship. List the confirmed findings ordered by severity, each with its location and failure scenario, then any open questions. If nothing survived verification, say so plainly instead of inventing nitpicks.`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Second opinion",
|
||||||
|
description: "Plan and build with a separate reviewer until it approves.",
|
||||||
|
body: `Whoever writes never approves their own work. One helper writes, a different, fresh helper checks.
|
||||||
|
|
||||||
|
1. Plan. run_subagent "planner" (model_tier code for software, standard otherwise) writes a numbered plan for the user's goal: steps, files or parts touched, how it will be verified.
|
||||||
|
2. Review the plan. run_subagent "reviewer" with only the goal and the plan, never the planner's reasoning, and a different model_tier from the planner when one is mapped. It returns APPROVED or a list of concrete findings.
|
||||||
|
3. Revise. Pass the findings back to a planner and review again. Stop at APPROVED or after five rounds; after five, show the open findings and ask the user how to proceed.
|
||||||
|
4. Swap roles. Build exactly the approved plan yourself or with a "builder" helper. Then run_subagent "inspector", a fresh helper that never saw the build, with the plan and the resulting diff or output. It checks every plan step and returns INSPECTED with zero findings or the findings. Fix and inspect again until clean.
|
||||||
|
5. Show the outcome with show_card: title "Second opinion", facts for plan rounds, findings fixed, and the verdict; checklist of the plan steps as inspected. Keep prose to one line.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Ship",
|
||||||
|
description: "Build software in phases: define, plan, build, verify, review, ship.",
|
||||||
|
body: `Move through six phases in order and show where you are. Before starting, show_card titled "Ship" with a checklist of the six phases; at each phase change, show it again with the finished phases ticked.
|
||||||
|
|
||||||
|
1. Define. Restate the goal, what done looks like, and what is out of scope. Ask at most one ask_user if a product choice has no sensible default.
|
||||||
|
2. Map, then plan. Before reading files one by one, map the codebase once on the computer (shell: list files, grep for the names involved) and note the few files that matter. Plan the smallest change that reaches the goal.
|
||||||
|
3. Build with the least code. Before writing, check: can existing code do it, can it be one line, can it be deleted instead, is any new abstraction actually used twice? Prefer the answer with fewer lines and fewer files.
|
||||||
|
4. Verify. Run the project's own tests, lint and typecheck with shell. Add a test for the new behavior. Do not continue while anything is red.
|
||||||
|
5. Review. run_subagent "reviewer", a fresh helper with only the goal and the diff, checks correctness, security and needless code. Fix what it confirms and verify again.
|
||||||
|
6. Ship. Commit with a message that says why, push, and report in one line what changed and how it was verified.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Founder",
|
||||||
|
description: "Test a business idea with simulated roles and buyers before spending.",
|
||||||
|
body: `Run the idea through a simulated team and a crowd of simulated buyers, then give a plain verdict. Every number is an estimate from the simulation; say so once on the final card. Never impersonate real people: use persona types such as "growth investor" or "product purist", never names or photos.
|
||||||
|
|
||||||
|
1. Clarify in one ask_user at most: the idea or ideas, place, price range, budget. If the user gave several ideas, run steps 2 to 6 for each and compare them at the end, best first.
|
||||||
|
2. Team. run_subagent once per role, each with the idea and the facts so far, each returning a short result:
|
||||||
|
- Board: three persona types argue for and against; return the strongest objection and the decision.
|
||||||
|
- Marketing: channels and the first message.
|
||||||
|
- CFO: cost per unit, price, and margin per product.
|
||||||
|
- Pricing and offer: the price point and the entry offer.
|
||||||
|
- Competition: who else serves these buyers and why they would switch.
|
||||||
|
- Brand: name, promise, tone.
|
||||||
|
- Operations: what has to run every day and what it costs.
|
||||||
|
- Launch: a launch date and the first two weeks.
|
||||||
|
3. Target buyer. Derive the target customer (who, need, frequency) and show it with show_card as a checklist the user can adjust; a reply changes the profile.
|
||||||
|
4. Buyer crowd. Simulate 100 buyers in batches of 10 with run_subagent (model_tier fast). Spread them over realistic income bands and habits. Each buyer returns buys or does not buy, the amount, and one objection in their own words. Keep a tally: scenario n/100, buys, does not buy, revenue.
|
||||||
|
5. Why not. Group the objections into reasons (price, existing habit, taste, distance, opening hours, trust) with counts. render_plot the income bands and the reasons.
|
||||||
|
6. Cost of failure. List what is lost if it does not work: rent, equipment, marketing, stock, with a total.
|
||||||
|
7. Result. show_card titled "Business plan · <name>": facts for target buyer, price, main strength, launch date, conversion from the crowd, monthly revenue estimate, cost of failure, and the verdict PROFITABLE or NOT PROFITABLE. Add up to three actions such as "Change price", "Test another idea", "Make a launch plan".`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const BUILTIN_AGENT_SKILLS: Array<{
|
export const BUILTIN_AGENT_SKILLS: Array<{
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { buildSkillMd, formatSkillsCatalogInstruction, parseSkillMd } from "@rakazo/core";
|
import { buildSkillMd, formatSkillsCatalogInstruction, parseSkillMd } from "@rakazo/core";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { BUILTIN_AGENT_SKILLS } from "./builtin-skills.js";
|
import { BUILTIN_AGENT_SKILLS } from "./builtin-skills.js";
|
||||||
|
import { builtinAgentTools } from "./builtin-tools.js";
|
||||||
import {
|
import {
|
||||||
listAgentSkillRecords,
|
listAgentSkillRecords,
|
||||||
skillCreateFromTool,
|
skillCreateFromTool,
|
||||||
@ -218,6 +219,23 @@ describe("skill tools", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("builtin skills only call tools the agent has", () => {
|
||||||
|
const tools = new Set(builtinAgentTools.map((tool) => tool.name));
|
||||||
|
const params = new Set(
|
||||||
|
builtinAgentTools.flatMap((tool) =>
|
||||||
|
Object.keys((tool.inputSchema as { properties?: object }).properties ?? {}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
for (const skill of BUILTIN_AGENT_SKILLS) {
|
||||||
|
for (const [called] of skill.content.matchAll(/\b[a-z]+_[a-z_]+\b/g)) {
|
||||||
|
expect(tools.has(called) || params.has(called), `${skill.name} calls ${called}`).toBe(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(BUILTIN_AGENT_SKILLS.map((skill) => skill.name)).toEqual(
|
||||||
|
expect.arrayContaining(["Second opinion", "Ship", "Founder"]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("ships builtin skills as valid, readable, read-only SKILL.md records", async () => {
|
it("ships builtin skills as valid, readable, read-only SKILL.md records", async () => {
|
||||||
expect(BUILTIN_AGENT_SKILLS.map((skill) => skill.name)).toContain("Interrogate");
|
expect(BUILTIN_AGENT_SKILLS.map((skill) => skill.name)).toContain("Interrogate");
|
||||||
for (const skill of BUILTIN_AGENT_SKILLS) {
|
for (const skill of BUILTIN_AGENT_SKILLS) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user