mirror of
https://github.com/openprose/prose.git
synced 2026-09-19 05:55:05 +08:00
66fe7a85e6
Ships the 13 wired tier-1/tier-2 examples under skills/open-prose/examples
with a hermetic offline gate at zero model spend:
- skills/open-prose/examples/{agent-observatory, basic-unit-suite,
forme-fixpoint, github-star-enricher, implementation-pipeline,
inbox-triage, masked-relay, monorepo-ci, oblique-weave, renewal-risk,
research-tree, surprise-cost, tamper-forge}: source .prose.md contracts,
committed replay/ state-dirs, and co-located deterministic tier-2
ledger-replay tests driving the real @openprose/reactor reconciler.
- tests/open-prose/examples/intelligent-react-examples-corpus.test.ts:
tier-1 doc-conformance over the 13 OWNED examples (agent-observatory
and forme-fixpoint normalized to lead each ### Continuity body with the
canonical input-driven/self-driven token; gateways stay external-driven).
- tools/eval-harness/: deterministic offline eval checker (10/10).
- vitest.config.ts: aliases @openprose/reactor public subpaths to the
prebuilt workspace dist; collects example *.test.ts; excludes key-gated
*.live.test.ts so the gate stays hermetic.
- package.json: adds test:examples (REACTOR_OFFLINE=1 vitest + eval
harness) and folds it into test.
- .github/workflows/ci-examples-gate.yml: runs the offline gate on
example/test path changes (no judge key).
- prune: removes the superseded packages/std/examples/{composed-reviewer,
confident-writer} .prose.md set.
Gates green: pnpm build clean; REACTOR_OFFLINE=1 pnpm test:examples =
17 vitest files / 214 tests + eval-harness 10/10; REACTOR_OFFLINE=1
pnpm test:skill = 14 files / 252 tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
102 lines
3.5 KiB
JavaScript
102 lines
3.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// cli.mjs — the integrator-wireable entry point.
|
|
//
|
|
// Usage:
|
|
// node tools/eval-harness/cli.mjs \
|
|
// --example masked-relay=packages/reactor-devtools/fixtures/masked-relay \
|
|
// [--example NAME=PATH ...] \
|
|
// [--scenarios cold_start,no_change_replay,...] \
|
|
// [--out reports/eval-report.md] \
|
|
// [--env /Users/sl/code/openprose/.env]
|
|
//
|
|
// Defaults to the shipped devtools fixtures when no --example is given, so a
|
|
// keyless `node tools/eval-harness/cli.mjs` is a self-contained offline smoke.
|
|
// The LLM judge path is OFF unless an OpenRouter key is resolvable AND
|
|
// REACTOR_OFFLINE is unset. The key value is NEVER printed.
|
|
|
|
import { join } from "node:path";
|
|
|
|
import { runEval, judgesEnabled } from "./index.mjs";
|
|
import { renderMarkdown, writeReport } from "./report.mjs";
|
|
import { REPO_ROOT, DEFAULT_ENV_PATH } from "./resolve.mjs";
|
|
|
|
function parseArgs(argv) {
|
|
const out = { examples: [], scenarios: undefined, out: undefined, env: DEFAULT_ENV_PATH };
|
|
for (let i = 0; i < argv.length; i += 1) {
|
|
const a = argv[i];
|
|
if (a === "--example") {
|
|
const v = argv[++i] ?? "";
|
|
const eq = v.indexOf("=");
|
|
if (eq > 0) {
|
|
out.examples.push({ exampleId: v.slice(0, eq), stateDir: v.slice(eq + 1) });
|
|
}
|
|
} else if (a === "--scenarios") {
|
|
out.scenarios = (argv[++i] ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
} else if (a === "--out") {
|
|
out.out = argv[++i];
|
|
} else if (a === "--env") {
|
|
out.env = argv[++i];
|
|
} else if (a === "--help" || a === "-h") {
|
|
out.help = true;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function defaultExamples() {
|
|
const fx = (n) => join(REPO_ROOT, "packages", "reactor-devtools", "fixtures", n);
|
|
// The shipped fixtures with topology + receipts. monorepo-ci / inbox-triage
|
|
// carry a `failed` receipt → exercise blocked_or_gated.
|
|
return [
|
|
{ exampleId: "masked-relay", stateDir: fx("masked-relay") },
|
|
{ exampleId: "contract-redline", stateDir: fx("contract-redline") },
|
|
{ exampleId: "monorepo-ci", stateDir: fx("monorepo-ci") },
|
|
];
|
|
}
|
|
|
|
async function main() {
|
|
const args = parseArgs(process.argv.slice(2));
|
|
if (args.help) {
|
|
process.stdout.write(
|
|
"reactor-eval-harness — deterministic-first eval over committed replay state-dirs\n" +
|
|
" --example NAME=PATH add an example (repeatable)\n" +
|
|
" --scenarios a,b,c restrict scenario kinds\n" +
|
|
" --out PATH write the markdown report\n" +
|
|
" --env PATH .env to read the OpenRouter key from (judges, key-gated)\n",
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
const examples = (args.examples.length ? args.examples : defaultExamples()).map(
|
|
(e) => ({ ...e, scenarios: args.scenarios }),
|
|
);
|
|
|
|
const on = judgesEnabled(args.env);
|
|
process.stderr.write(
|
|
`[eval-harness] LLM judges: ${on ? "ON (key resolved)" : "OFF (no key / REACTOR_OFFLINE) — deterministic-only"}\n`,
|
|
);
|
|
|
|
const { report } = await runEval({
|
|
harnessBuildId: process.env["EVAL_HARNESS_BUILD_ID"] ?? "local",
|
|
examples,
|
|
envPath: args.env,
|
|
});
|
|
|
|
if (args.out) {
|
|
const written = writeReport(report, args.out);
|
|
process.stderr.write(`[eval-harness] wrote ${written.path} (${written.contentHash})\n`);
|
|
} else {
|
|
process.stdout.write(renderMarkdown(report));
|
|
}
|
|
|
|
// Exit non-zero if any scenario failed (CI-safe signal).
|
|
return report.summary.failed > 0 ? 1 : 0;
|
|
}
|
|
|
|
main()
|
|
.then((code) => process.exit(code))
|
|
.catch((err) => {
|
|
process.stderr.write(`[eval-harness] fatal: ${err?.stack ?? err}\n`);
|
|
process.exit(2);
|
|
});
|