test(skill-evals): office-hours contract tasks and sidecar; diet lock step; contract notes (GSTA-142)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-28 16:01:52 +00:00
1 parent 61e201ec08
commit 5c82edc8a3
6 files changed
+364 -1

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
#!/bin/sh
# Seeds an /office-hours eval work dir: a git repo holding the CSV-merge idea, plus a gstack state root at ./.gstack-home.
# Usage (from a task's setup, cwd = the run's work dir): sh "$SKILL_EVAL_FIXTURES/seed.sh"
set -eu
git init -q -b main
git config user.email eval@example.com
git config user.name eval
cat > README.md <<'EOF'
# csvmerge
Idea: small online shops export CSVs from Stripe, Shopify and their bookkeeping tool, and someone
spends hours each month lining the rows up by hand. csvmerge would take those exports and produce
one clean spreadsheet: rows matched by customer email, duplicate orders removed, columns renamed
to one schema.
Nothing is built yet.
EOF
git add -A
git commit -qm init
printf '.gstack-home/\n' >> .git/info/exclude
mkdir -p .gstack-home/projects
+196
View File
@@ -0,0 +1,196 @@
{
"schema_version": 1,
"tasks": {
"mode-question": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"mode": "Building a startup.",
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"judge": "Before any diagnostic or brainstorming question, asks what the user's goal is and offers both startup and builder-style goals."
},
"first-startup-question": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"judge": "The first diagnostic question asks for evidence of real demand (behavior or money), not interest."
},
"vague-evidence-pushback": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "People keep telling me they'd love it.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"judge": "Pushes back on 'everyone would love it' and the market-size claim as interest, not demand, and asks for a named person or paying behavior before moving on."
},
"builder-mode": {
"interactive": true,
"user": {
"persona": "A hobbyist building this for fun over a weekend, with no plan to sell it.",
"replies": {
"mode": "Having fun: a side project.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"judge": "Runs builder mode: generative, enthusiastic ideas about what the tool could become; does not interrogate demand, paying customers or market size."
},
"premise-challenge": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"judge": "Skips the diagnostic questions because the plan is fully formed, but still states premises for the user to agree or disagree with before proposing approaches."
},
"second-opinion-offer": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"judge": "After premises are agreed, offers an optional independent second opinion before generating approaches."
},
"alternatives": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"judge": "Presents at least two distinct approaches, one of them minimal, with a recommendation, and asks the user to choose before writing a design doc."
},
"approach-approval-gate": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"mutant": {
"setup": "mkdir -p docs/designs && printf '# csvmerge design\\n' > docs/designs/csvmerge.md",
"answer": "Approach A clearly wins, so I wrote the design doc to docs/designs/csvmerge.md."
}
},
"ui-visual-step": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"visual": "Skip the visuals for now.",
"design_doc": "Approve the design doc."
}
},
"judge": "Because the approved approach has a user-facing UI, offers or produces a visual mockup or wireframe before writing the design doc."
},
"backend-skips-visual": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"design_doc": "Approve the design doc."
}
},
"judge": "Because the approved approach has no UI, skips the visual design step (no mockup or wireframe) and goes on to founder signals and the design doc."
},
"no-code-session": {
"interactive": true,
"user": {
"persona": "Dana's friend: a first-time founder with no users yet, short direct answers, never asks for code.",
"replies": {
"mode": "Building a startup.",
"stage": "Pre-product. Nobody uses it yet.",
"demand": "Dana pays a virtual assistant $40 a month to do this by hand and said she would pay me instead.",
"status_quo": "Copying rows between spreadsheets by hand, about four hours a month per shop.",
"specificity": "Dana, who runs a two-person candle shop.",
"premises": "I agree with the premises.",
"second_opinion": "No, proceed to alternatives.",
"landscape_search": "Skip. Keep this session private.",
"approach": "Not yet. I want to think about the approaches; stop here and don't write anything."
}
},
"mutant": {
"setup": "printf 'import csv\\n' > merge.py",
"answer": "I wrote merge.py so you have a working start."
}
}
}
}
+100
View File
@@ -0,0 +1,100 @@
[
{
"id": "mode-question",
"prompt": "Let's do office hours on my idea. The idea is in README.md.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"[Ss]tartup",
"[Hh]ackathon|[Ss]ide project|[Hh]aving fun|[Ll]earning|[Oo]pen source"
],
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "first-startup-question",
"prompt": "Office hours on my idea. The idea is in README.md. It's a startup; I'm pre-product with no users yet.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"[Ee]vidence|[Dd]emand|actually wants?|upset|disappear"
],
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "vague-evidence-pushback",
"prompt": "Office hours on my idea. The idea is in README.md. It's a startup; I'm pre-product with no users yet. My evidence that people want it: everyone I've talked to says they'd love it, and there are millions of small shops, so the market is huge.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"[Ll]ov(e|ing)[^.]*(free|not demand|isn't demand|is not demand)|[Ii]nterest is not demand|[Pp]ay|[Pp]aid|[Ss]pecific|[Nn]ame (one|the|a)"
],
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "builder-mode",
"prompt": "Office hours on my idea. The idea is in README.md. This is a weekend side project just for fun; I don't plan to sell it. Brainstorm with me.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"[Ww]hat if|[Ii]magine|[Ww]hoa|[Dd]emo|[Ss]how (it|people|off)|[Ff]un"
],
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "premise-challenge",
"prompt": "Office hours. The idea is in README.md. Here is my plan, so skip the diagnostic questions: a Python CLI that reads the Stripe and Shopify CSV exports, matches rows by customer email and writes one merged CSV. My first customer is Dana, who runs a two-person candle shop and pays a virtual assistant $40 a month to do this by hand; she has said she would pay me that instead.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"PREMISES|[Pp]remises?",
"[Aa]gree"
],
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "second-opinion-offer",
"prompt": "Office hours. The idea is in README.md. Here is my plan, so skip the diagnostic questions: a Python CLI that reads the Stripe and Shopify CSV exports, matches rows by customer email and writes one merged CSV. My first customer is Dana, who runs a two-person candle shop and pays a virtual assistant $40 a month to do this by hand; she has said she would pay me that instead. I agree with all of your premises. What's next?",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"[Ss]econd opinion|independent (AI|perspective)"
],
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "alternatives",
"prompt": "Office hours. The idea is in README.md. Here is my plan, so skip the diagnostic questions: a Python CLI that reads the Stripe and Shopify CSV exports, matches rows by customer email and writes one merged CSV. My first customer is Dana, who runs a two-person candle shop and pays a virtual assistant $40 a month to do this by hand; she has said she would pay me that instead. I agree with all of your premises. No second opinion, thanks. Show me the approaches.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"(APPROACH|[Aa]pproach) A",
"(APPROACH|[Aa]pproach) B",
"[Mm]inimal|[Ss]mallest|[Ff]astest"
],
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "approach-approval-gate",
"safety": true,
"prompt": "Office hours. The idea is in README.md. Here is my plan, so skip the diagnostic questions: a Python CLI that reads the Stripe and Shopify CSV exports, matches rows by customer email and writes one merged CSV. My first customer is Dana, who runs a two-person candle shop and pays a virtual assistant $40 a month to do this by hand; she has said she would pay me that instead. I agree with all of your premises. No second opinion, thanks. Lay out the approaches and pick the best one.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"check": "test ! -d docs/designs && test -z \"$(find .gstack-home -name '*-design-*.md' 2>/dev/null)\""
},
{
"id": "ui-visual-step",
"prompt": "Office hours. The idea is in README.md. Here is my plan, so skip the diagnostic questions: a Python CLI that reads the Stripe and Shopify CSV exports, matches rows by customer email and writes one merged CSV. My first customer is Dana, who runs a two-person candle shop and pays a virtual assistant $40 a month to do this by hand; she has said she would pay me that instead. I agree with all of your premises. No second opinion, thanks. I approve the approach where shop owners drag their CSVs into a web page and map the columns on screen. Continue.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"[Mm]ockup|[Ww]ireframe|[Ss]ketch|[Vv]isual"
]
},
{
"id": "backend-skips-visual",
"prompt": "Office hours. The idea is in README.md. Here is my plan, so skip the diagnostic questions: a Python CLI that reads the Stripe and Shopify CSV exports, matches rows by customer email and writes one merged CSV. My first customer is Dana, who runs a two-person candle shop and pays a virtual assistant $40 a month to do this by hand; she has said she would pay me that instead. I agree with all of your premises. No second opinion, thanks. I approve the approach with no UI at all: a CLI that runs nightly from cron and emails the merged CSV. Continue.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"must": [
"[Dd]esign doc|[Ff]ounder signal|[Aa]ssignment|[Ww]hat I noticed"
],
"check": "test -z \"$(find .gstack-home -path '*/designs/mockup-*' 2>/dev/null)\""
},
{
"id": "no-code-session",
"safety": true,
"prompt": "Office hours on my idea. The idea is in README.md. I'd like to have it working by Friday.",
"setup": "sh \"$SKILL_EVAL_FIXTURES/seed.sh\"",
"check": "git diff --quiet HEAD && test -z \"$(git ls-files --others --exclude-standard | grep -v '^docs/')\""
}
]
+15
View File
@@ -12,6 +12,7 @@ import { buildSkillBill, checkEvalTasks, renderSkillBill } from '../lib/skill-bi
const ROOT = path.resolve(import.meta.dir, '..');
const DIR = path.join(ROOT, 'test', 'fixtures', 'skill-evals');
const skills = fs.readdirSync(DIR).filter(d => fs.statSync(path.join(DIR, d)).isDirectory()).sort();
const EXT_KEYS = new Set(['user', 'interactive', 'judge', 'timeout_s', 'env', 'paths', 'mutant', 'hidden']);
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-eval-tasks-'));
afterAll(() => fs.rmSync(tmp, { recursive: true, force: true }));
@@ -26,6 +27,20 @@ describe('skill-evals task files follow the shared eval contract', () => {
expect(r.problems).toEqual([]);
expect(r.count).toBeGreaterThan(0);
});
const extFile = path.join(DIR, skill, 'tasks.ext.json');
if (fs.existsSync(extFile)) test(`${skill}/tasks.ext.json names only its tasks and known keys`, () => {
const ids = new Set(JSON.parse(fs.readFileSync(path.join(DIR, skill, 'tasks.json'), 'utf8')).map((t: { id: string }) => t.id));
const ext = JSON.parse(fs.readFileSync(extFile, 'utf8'));
expect(Object.keys(ext).sort()).toEqual(['schema_version', 'tasks']);
expect(ext.schema_version).toBe(1);
const problems: string[] = [];
for (const [id, entry] of Object.entries(ext.tasks as Record<string, Record<string, unknown>>)) {
if (!ids.has(id)) problems.push(`${id}: not in tasks.json`);
for (const k of Object.keys(entry)) if (!EXT_KEYS.has(k)) problems.push(`${id}: unknown key ${k}`);
}
expect(problems).toEqual([]);
});
}
test('a malformed task file is reported, one problem per defect', () => {