Skip to content

Commit bbd390c

Browse files
authored
Merge pull request #54 from hookdeck/skip-without-seed-credentials
Skip a scenario whose seed credentials are missing, rather than failing it
2 parents d5766ca + a31c7aa commit bbd390c

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

.github/workflows/eval-refresh.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,14 @@ jobs:
303303
# on 13 August scored outpost-001 as six agent failures rather than
304304
# skipping it, which is a false result published against named vendors.
305305
OUTPOST_API_KEY: ${{ secrets.OUTPOST_API_KEY }}
306+
# Synthetic AWS credentials a scenario's workspace note carries as
307+
# `${SEED_*}` placeholders. Not real, and not committed: a key realistic
308+
# enough to convince an agent is realistic enough to trip push
309+
# protection. Absent, `benchmark-outpost-004` is skipped rather than
310+
# failed — an agent handed a literal placeholder is right to refuse, and
311+
# scoring that is the 13 August mistake in a new form.
312+
SEED_ACME_SQS_ACCESS_KEY: ${{ secrets.SEED_ACME_SQS_ACCESS_KEY }}
313+
SEED_ACME_SQS_SECRET_KEY: ${{ secrets.SEED_ACME_SQS_SECRET_KEY }}
306314
# For `gh run cancel` when a provider stops answering. Needs
307315
# `actions: write` at the workflow level, which `publish-results` did
308316
# not previously require.
@@ -354,6 +362,8 @@ jobs:
354362
echo "HOOKDECK_API_KEY=${HOOKDECK_API_KEY}"
355363
echo "HOOKDECK_WEBHOOK_SECRET=${HOOKDECK_WEBHOOK_SECRET}"
356364
echo "OUTPOST_API_KEY=${OUTPOST_API_KEY}"
365+
echo "SEED_ACME_SQS_ACCESS_KEY=${SEED_ACME_SQS_ACCESS_KEY}"
366+
echo "SEED_ACME_SQS_SECRET_KEY=${SEED_ACME_SQS_SECRET_KEY}"
357367
} > .env
358368
359369
- name: Run evals

apps/framework/harness/run-eval.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,50 @@ function unmetRequirements(ev: EvalManifest): string[] {
108108
const available: Record<string, boolean> = {
109109
outpost: Boolean(process.env.OUTPOST_API_KEY),
110110
};
111-
return (ev.metadata.requires ?? []).filter((r) => !available[r]);
111+
const unmet = (ev.metadata.requires ?? []).filter((r) => !available[r]);
112+
113+
// A `${SEED_*}` placeholder the environment cannot fill is an unmet
114+
// requirement, not a task.
115+
//
116+
// `local/` files leave an unset variable as written, so the agent is handed
117+
// the literal `${SEED_ACME_SQS_ACCESS_KEY}`. That reads as an obvious
118+
// placeholder, and an agent that notices is right to stop rather than
119+
// configure delivery that will silently fail — measured on 24 August, where
120+
// exactly that scored 0/1 while three agents that did not notice scored 6/6.
121+
//
122+
// Scoring it is the 13 August mistake again: a missing credential became six
123+
// agent failures against named vendors, and the fix then was to skip rather
124+
// than score. The same reasoning applies to a credential the scenario
125+
// supplies itself.
126+
return [...unmet, ...unfilledSeedPlaceholders(ev)];
127+
}
128+
129+
/** `SEED_*` names a scenario's workspace asks for and the environment lacks. */
130+
function unfilledSeedPlaceholders(ev: EvalManifest): string[] {
131+
if (!ev.localDir || !existsSync(ev.localDir)) return [];
132+
const missing = new Set<string>();
133+
134+
const walk = (dir: string) => {
135+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
136+
const path = join(dir, entry.name);
137+
if (entry.isDirectory()) {
138+
walk(path);
139+
continue;
140+
}
141+
let text: string;
142+
try {
143+
text = readFileSync(path, 'utf8');
144+
} catch {
145+
continue; // binary; nothing to expand
146+
}
147+
for (const [, name] of text.matchAll(/\$\{(SEED_[A-Z0-9_]+)\}/g)) {
148+
if (!process.env[name]) missing.add(name);
149+
}
150+
}
151+
};
152+
walk(ev.localDir);
153+
154+
return [...missing];
112155
}
113156

114157
type ToolsSkill = { name: string; description: string; body: string };

0 commit comments

Comments
 (0)