- Dark Provenance whitepaper: complete sections 2-6 (What Is Dark Provenance, The Speckl Approach, Semantic Preservation & SA-11, Implications, Call to Action) - README: public launch-ready with architecture, roadmap, getting started - SpeckDL SPEC.md: v0.1 language specification with EBNF grammar, type system, compilation pipeline, 5 example Specks - Reference agent: TypeScript scaffold with Orchestrator, TeamLead, SMEs (VerificationSME, CodegenSME), TillDone condition tracking, ProvenanceGraph, and CLI with example Specks
180 lines
No EOL
5.6 KiB
JavaScript
180 lines
No EOL
5.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Speckl Reference Agent — CLI entry point.
|
|
*
|
|
* Usage:
|
|
* npx tsx src/cli.ts examples/ --speck RetryHandler
|
|
* npx tsx src/cli.ts --help
|
|
*/
|
|
|
|
import { Orchestrator } from "./orchestrator.js";
|
|
import type { SpeckDefinition, Constraint, VerifyCondition } from "./types.js";
|
|
|
|
// ─── Example Specks ─────────────────────────────────────────
|
|
|
|
const exampleSpecks: Record<string, SpeckDefinition> = {
|
|
RetryHandler: {
|
|
name: "RetryHandler",
|
|
input: {
|
|
operation: { kind: "named", name: "Operation" },
|
|
maxRetries: "Nat",
|
|
},
|
|
output: { kind: "named", name: "Result" },
|
|
constraints: [
|
|
{ id: "c1", expression: "maxRetries <= 5" },
|
|
{ id: "c2", expression: "backoff(delay(n)) == delay(n-1) * 2" },
|
|
{ id: "c3", expression: "totalDelay < 30s" },
|
|
] as Constraint[],
|
|
verify: [
|
|
{ id: "v1", kind: "always", expression: "implies(attempt > 1, delay > 0)" },
|
|
] as VerifyCondition[],
|
|
},
|
|
|
|
RateLimiter: {
|
|
name: "RateLimiter",
|
|
input: {
|
|
requests: { kind: "list", element: { kind: "named", name: "Request" } },
|
|
window: "Nat",
|
|
maxRequests: "Nat",
|
|
},
|
|
output: {
|
|
kind: "record",
|
|
fields: {
|
|
allowed: { kind: "list", element: "Bool" },
|
|
remaining: "Nat",
|
|
},
|
|
},
|
|
constraints: [
|
|
{ id: "c1", expression: "maxRequests > 0" },
|
|
{ id: "c2", expression: "window > 0" },
|
|
{ id: "c3", expression: "count(allowed, true) <= maxRequests" },
|
|
{ id: "c4", expression: "remaining == maxRequests - count(allowed, true)" },
|
|
] as Constraint[],
|
|
verify: [
|
|
{ id: "v1", kind: "always", expression: "implies(remaining == 0, not(allowed[current]))" },
|
|
] as VerifyCondition[],
|
|
},
|
|
|
|
DrugDosageCalculator: {
|
|
name: "DrugDosageCalculator",
|
|
input: {
|
|
patientWeight: "Real",
|
|
drugConcentration: "Real",
|
|
prescribedDose: "Real",
|
|
minDose: "Real",
|
|
maxDose: "Real",
|
|
frequency: "Nat",
|
|
},
|
|
output: {
|
|
kind: "record",
|
|
fields: {
|
|
volume: "Real",
|
|
safe: "Bool",
|
|
},
|
|
},
|
|
constraints: [
|
|
{ id: "c1", expression: "patientWeight > 0" },
|
|
{ id: "c2", expression: "drugConcentration > 0" },
|
|
{ id: "c3", expression: "minDose > 0" },
|
|
{ id: "c4", expression: "maxDose > minDose" },
|
|
{ id: "c5", expression: "frequency >= 1" },
|
|
{ id: "c6", expression: "frequency <= 6" },
|
|
{ id: "c7", expression: "implies(safe, volume >= minDose / drugConcentration)" },
|
|
{ id: "c8", expression: "implies(safe, volume <= maxDose / drugConcentration)" },
|
|
{ id: "c9", expression: "implies(not safe, volume == 0)" },
|
|
] as Constraint[],
|
|
verify: [
|
|
{ id: "v1", kind: "always", expression: "implies(prescribedDose >= minDose and prescribedDose <= maxDose, safe)" },
|
|
{ id: "v2", kind: "always", expression: "implies(not safe, volume == 0)" },
|
|
] as VerifyCondition[],
|
|
events: [
|
|
{
|
|
name: "DoseCalculated",
|
|
fields: { volume: "Real", safe: "Bool" },
|
|
},
|
|
{
|
|
name: "DoseRejected",
|
|
fields: { reason: "String" },
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
// ─── Main ────────────────────────────────────────────────────
|
|
|
|
async function main(): Promise<void> {
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.includes("--help") || args.includes("-h")) {
|
|
console.log(`
|
|
Speckl Reference Agent v0.1
|
|
|
|
Usage:
|
|
npx tsx src/cli.ts [options]
|
|
|
|
Options:
|
|
--speck <name> Process a specific example Speck
|
|
--list List available example Specks
|
|
--provenance Show provenance graph after processing
|
|
--help Show this help message
|
|
|
|
Example Specks:
|
|
${Object.keys(exampleSpecks).join("\n ")}
|
|
`);
|
|
return;
|
|
}
|
|
|
|
if (args.includes("--list")) {
|
|
console.log("Available example Specks:");
|
|
for (const [name, speck] of Object.entries(exampleSpecks)) {
|
|
console.log(` ${name}: ${speck.constraints.length} constraints, ${speck.verify.length} verify conditions`);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const speckName = args[args.indexOf("--speck") + 1] || "RetryHandler";
|
|
const speck = exampleSpecks[speckName];
|
|
|
|
if (!speck) {
|
|
console.error(`Unknown Speck: ${speckName}`);
|
|
console.error(`Available: ${Object.keys(exampleSpecks).join(", ")}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`\n🧬 Processing Speck: ${speck.name}`);
|
|
console.log(` Constraints: ${speck.constraints.length}`);
|
|
console.log(` Verify conditions: ${speck.verify.length}`);
|
|
console.log(` Events: ${speck.events?.length ?? 0}\n`);
|
|
|
|
const orchestrator = new Orchestrator();
|
|
const result = await orchestrator.processSpeck(speck);
|
|
|
|
console.log(`\n✅ Result: ${result.status}`);
|
|
console.log(` Duration: ${result.durationMs}ms`);
|
|
|
|
for (const [taskId, taskResult] of Object.entries(result.results)) {
|
|
const r = taskResult as any;
|
|
console.log(`\n Task ${taskId}: ${r.status}`);
|
|
if (r.output) {
|
|
if (r.output.z3Result) {
|
|
console.log(` Z3 result: ${r.output.z3Result}`);
|
|
console.log(` All verified: ${r.output.allVerified ?? "N/A"}`);
|
|
}
|
|
if (r.output.code) {
|
|
console.log(` Generated ${r.output.language} code (${r.output.code.split("\n").length} lines)`);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (args.includes("--provenance")) {
|
|
console.log(`\n📊 Provenance Graph:`);
|
|
console.log(JSON.stringify(result.provenance, null, 2));
|
|
}
|
|
|
|
console.log();
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("Fatal error:", err);
|
|
process.exit(1);
|
|
}); |