voyage/evals/skill-interviewing-effect-round/scaffold.sh
Kjell Tore Guttormsen f6e18a1d09
feat(skills): interviewing - one source for the brief's question round (order 7e)
/trekbrief Phase 3 carried the interview form inline. It now asks for the
new skill voyage:interviewing by name (allowed-tools gains Skill) and keeps
only its own contract: the size class and budget, the three fixed questions
(framing first, class, storage), where each answer lands in the brief, and
the answers that decide nothing. Step 3a and the form points of Step 3c move
into the skill with their credits (Pocock, Osmani, Spec Kit; licences and
line ranges checked at the pinned commits).

Red baseline first (3 helper interviews against a fixed answer sheet,
Opus 5.5): 10-11 questions in round 1 (3 of 3), 15 of 31 without a
recommendation, a hollow answer written in as a decision (2 of 3), unasked
scope as a requirement (2 of 3). With the skill: 4, 0 of 12, 0 of 3, 0 of 3.
Fact questions and stopping did not fail without it.

Also: pointers to voyage:writing-criteria (S3) where criteria are asked and
written, both review skill names (S3 + S4) in the brief-reviewer prompt; the
intent pins that read moved text now read the skill, same assertions; the
Phase 7 line_ref moves with the shorter Phase 3; 7 eval cases (3/2/2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 07:44:42 +02:00

92 lines
4 KiB
Bash
Executable file

#!/bin/bash
# Seeds the empty run workspace with a small seed-library app (Node, node --test),
# made up from scratch. Self-contained on purpose: the run cannot read the eval
# directory, and the fixture is written inline, with no path outside the
# workspace and no git. The same project the red baseline ran against.
set -eu
mkdir -p src tests data
cat > package.json <<'J'
{ "name": "seedswap", "version": "0.4.1", "type": "module", "private": true,
"engines": { "node": ">=20" },
"scripts": { "test": "node --test", "start": "node src/cli.mjs" } }
J
cat > README.md <<'J'
# seedswap
A seed library for a garden club. Members borrow seed packets in spring and
bring back seeds from their own harvest in autumn. One volunteer runs this
command-line tool on the club laptop.
node src/cli.mjs borrow <member> <variety>
node src/cli.mjs return <member> <variety>
node src/cli.mjs list
Data lives in `data/*.json`. Messages to members go to `data/outbox.txt`,
which the volunteer prints and hands out at the monthly meeting.
J
cat > src/store.mjs <<'J'
import { readFileSync, writeFileSync } from 'node:fs';
const path = (name) => new URL(`../data/${name}.json`, import.meta.url);
export const load = (name) => JSON.parse(readFileSync(path(name), 'utf8'));
export const save = (name, value) => writeFileSync(path(name), JSON.stringify(value, null, 2) + '\n');
J
cat > src/members.mjs <<'J'
import { load } from './store.mjs';
// A member: { id, name, joined }. No email or phone is stored.
export const members = () => load('members');
export const byId = (id) => members().find((m) => m.id === id);
J
cat > src/loans.mjs <<'J'
import { load, save } from './store.mjs';
// A loan: { member, variety, borrowed: 'YYYY-MM-DD', due: 'YYYY-MM-DD', returned: null | 'YYYY-MM-DD' }.
// `due` is set to 15 October of the borrowing year (end of harvest).
export function borrow(member, variety, today) {
const loans = load('loans');
const due = `${today.slice(0, 4)}-10-15`;
loans.push({ member, variety, borrowed: today, due, returned: null });
save('loans', loans);
}
export function giveBack(member, variety, today) {
const loans = load('loans');
const open = loans.find((l) => l.member === member && l.variety === variety && !l.returned);
if (!open) throw new Error(`no open loan of ${variety} for ${member}`);
open.returned = today;
save('loans', loans);
}
export const openLoans = () => load('loans').filter((l) => !l.returned);
J
cat > src/notify.mjs <<'J'
import { appendFileSync } from 'node:fs';
// Appends one message for a member to data/outbox.txt (printed at meetings).
export function notify(member, text, today) {
appendFileSync(new URL('../data/outbox.txt', import.meta.url), `${today}\t${member.name}\t${text}\n`);
}
J
cat > src/cli.mjs <<'J'
import { borrow, giveBack, openLoans } from './loans.mjs';
const [cmd, member, variety] = process.argv.slice(2);
const today = new Date().toISOString().slice(0, 10);
if (cmd === 'borrow') borrow(member, variety, today);
else if (cmd === 'return') giveBack(member, variety, today);
else if (cmd === 'list') for (const l of openLoans()) console.log(`${l.member}\t${l.variety}\tdue ${l.due}`);
else { console.error('usage: borrow|return|list'); process.exit(2); }
J
cat > tests/loans.test.mjs <<'J'
import { test } from 'node:test';
import assert from 'node:assert/strict';
test('loans module loads', async () => {
const m = await import('../src/loans.mjs');
assert.equal(typeof m.borrow, 'function');
});
J
cat > data/members.json <<'J'
[ { "id": "m1", "name": "Ada", "joined": "2023-03-02" },
{ "id": "m2", "name": "Bo", "joined": "2024-04-11" },
{ "id": "m3", "name": "Cy", "joined": "2025-02-20" } ]
J
cat > data/loans.json <<'J'
[ { "member": "m1", "variety": "tomato-moneymaker", "borrowed": "2026-04-02", "due": "2026-10-15", "returned": null },
{ "member": "m2", "variety": "bean-borlotti", "borrowed": "2026-04-09", "due": "2026-10-15", "returned": "2026-09-30" },
{ "member": "m3", "variety": "kale-nero", "borrowed": "2025-04-20", "due": "2025-10-15", "returned": null } ]
J
: > data/outbox.txt