@multileaf/ai-workflow 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/context-benchmark.d.ts +52 -0
- package/dist/context-benchmark.js +170 -0
- package/dist/cost-attribution.d.ts +65 -0
- package/dist/cost-attribution.js +119 -0
- package/dist/model-routing.d.ts +38 -0
- package/dist/model-routing.js +116 -0
- package/dist/task-parity.d.ts +67 -0
- package/dist/task-parity.js +202 -0
- package/dist/traceability.d.ts +2 -0
- package/dist/traceability.js +35 -0
- package/dist/usage-sidecar.d.ts +56 -0
- package/dist/usage-sidecar.js +187 -0
- package/dist/workflow.js +304 -0
- package/docs/README.md +2 -0
- package/docs/token-efficiency-research.md +135 -0
- package/docs/token-efficiency-study-notes.md +128 -0
- package/docs/token-efficiency.md +104 -0
- package/package.json +1 -1
package/dist/workflow.js
CHANGED
|
@@ -37,6 +37,12 @@ import { createSummaryCache, isSummaryFresh, parseSummaryCache, serializeSummary
|
|
|
37
37
|
import { parseTokenUsage, recordTokenUsage, serializeTokenUsage, summarizeUsageByModel, } from "./token-usage.js";
|
|
38
38
|
import { APPROVAL_STAGES, assertWorkflowId, validateTaskTicket, approveStage, beginStageApproval, createApprovalLedger, getUnresolvedPrerequisites, invalidateStageAndDescendants, parseApprovalLedger, parseWorkflowSession, rejectStage, serializeApprovalLedger, serializeWorkflowSession, skipOptionalStage, stageDefinition, stageFingerprint, } from "./human-approval.js";
|
|
39
39
|
import { measureContextQuality, serializeContextQuality } from "./context-quality.js";
|
|
40
|
+
import { collectContextBench, compareContextBench, parseContextBench, serializeContextBench, } from "./context-benchmark.js";
|
|
41
|
+
import { materializeParityTask, parseParityTask, runParitySuite, serializeParitySuite, TASK_ASSEMBLER, } from "./task-parity.js";
|
|
42
|
+
import { appendUsageSidecarEntry, parseUsageSidecar, serializeUsageSidecar, serializeUsageSidecarSummary, summarizeUsageSidecar, USAGE_SIDECAR_PATH, USAGE_TIERS, } from "./usage-sidecar.js";
|
|
43
|
+
import { computeCostAttribution, serializeCostAttribution, serializeTaskUsage, summarizeTaskUsage, } from "./cost-attribution.js";
|
|
44
|
+
import { defaultRoutingTable, parseRoutingTable, resolveRoute, ROUTING_TABLE_PATH, serializeResolvedRoute, serializeRoutingTable, } from "./model-routing.js";
|
|
45
|
+
import { parseTraceabilityGraph } from "./traceability.js";
|
|
40
46
|
import { serializeAdapterCapabilities } from "./adapter-contract.js";
|
|
41
47
|
import { buildTraceabilityGraph, serializeTraceabilityGraph } from "./traceability.js";
|
|
42
48
|
import { evaluateQualityGate } from "./quality-gates.js";
|
|
@@ -530,6 +536,105 @@ export async function runCommand(args, root, fs, services = {}) {
|
|
|
530
536
|
}
|
|
531
537
|
return { exitCode: 1, error: usage };
|
|
532
538
|
}
|
|
539
|
+
if (command === "context-bench") {
|
|
540
|
+
if (!fs.exists(join(aiw, "manifest.yml")))
|
|
541
|
+
return { exitCode: 1, error: "Run `aiw install` first." };
|
|
542
|
+
const saveCount = args.filter((arg) => arg === "--save").length;
|
|
543
|
+
if (saveCount > 1)
|
|
544
|
+
return { exitCode: 1, error: "Usage: aiw context-bench [--baseline=path] [--save]" };
|
|
545
|
+
const save = saveCount === 1;
|
|
546
|
+
const parsed = parseNamedOptions(args.slice(1).filter((arg) => arg !== "--save"), ["baseline"]);
|
|
547
|
+
if (!parsed)
|
|
548
|
+
return { exitCode: 1, error: "Usage: aiw context-bench [--baseline=path] [--save]" };
|
|
549
|
+
const report = collectContextBench(root, fs);
|
|
550
|
+
const serialized = serializeContextBench(report);
|
|
551
|
+
const lines = [serialized.trimEnd()];
|
|
552
|
+
if (parsed.baseline) {
|
|
553
|
+
const baselinePath = safeProjectFile(root, parsed.baseline, fs);
|
|
554
|
+
if (!fs.exists(baselinePath))
|
|
555
|
+
return { exitCode: 1, error: `Baseline not found: ${parsed.baseline}` };
|
|
556
|
+
const comparison = compareContextBench(parseContextBench(fs.read(baselinePath)), report);
|
|
557
|
+
lines.push(`Baseline: ${parsed.baseline} (bundle ${comparison.bundleChanged ? "changed" : "identical"}, token delta ${comparison.totalTokenDelta >= 0 ? "+" : ""}${comparison.totalTokenDelta})`);
|
|
558
|
+
if (comparison.regressions.length)
|
|
559
|
+
return {
|
|
560
|
+
exitCode: 1,
|
|
561
|
+
error: `Standing context regressed against baseline:\n${comparison.regressions.map((regression) => `- ${regression}`).join("\n")}`,
|
|
562
|
+
output: lines.join("\n"),
|
|
563
|
+
};
|
|
564
|
+
lines.push("No regressions against baseline.");
|
|
565
|
+
}
|
|
566
|
+
if (save) {
|
|
567
|
+
const checkpoint = join(aiw, `checkpoints/context-bench-${report.bundleHash}.yml`);
|
|
568
|
+
if (!fs.createExclusive)
|
|
569
|
+
return { exitCode: 1, error: "The filesystem does not support atomic checkpoints." };
|
|
570
|
+
if (!fs.createExclusive(checkpoint, serialized)) {
|
|
571
|
+
if (fs.read(checkpoint) !== serialized)
|
|
572
|
+
return {
|
|
573
|
+
exitCode: 1,
|
|
574
|
+
error: `Checkpoint already exists with different content: ${checkpoint}`,
|
|
575
|
+
};
|
|
576
|
+
lines.push(`Checkpoint already recorded: ${checkpoint}`);
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
lines.push(`Checkpoint saved: ${checkpoint}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return { exitCode: 0, output: lines.join("\n") };
|
|
583
|
+
}
|
|
584
|
+
if (command === "parity") {
|
|
585
|
+
if (!fs.exists(join(aiw, "manifest.yml")))
|
|
586
|
+
return { exitCode: 1, error: "Run `aiw install` first." };
|
|
587
|
+
const parsed = parseNamedOptions(args.slice(1), ["tasks"]);
|
|
588
|
+
if (!parsed)
|
|
589
|
+
return { exitCode: 1, error: "Usage: aiw parity [--tasks=directory]" };
|
|
590
|
+
const tasksPath = parsed.tasks;
|
|
591
|
+
if (tasksPath && (isAbsolute(tasksPath) || tasksPath.split(/[\\/]/).includes("..")))
|
|
592
|
+
return {
|
|
593
|
+
exitCode: 1,
|
|
594
|
+
error: "Parity tasks directory must be a safe project-relative path.",
|
|
595
|
+
};
|
|
596
|
+
const tasksDirectory = tasksPath ? join(root, tasksPath) : join(aiw, "parity");
|
|
597
|
+
if (fs.pathType?.(tasksDirectory) !== "directory" || !fs.list)
|
|
598
|
+
return {
|
|
599
|
+
exitCode: 1,
|
|
600
|
+
error: `Parity tasks directory not found: ${relative(root, tasksDirectory).split(sep).join("/")}. Add YAML tasks (see docs/token-efficiency.md).`,
|
|
601
|
+
};
|
|
602
|
+
const taskFiles = (fs.list(tasksDirectory) ?? [])
|
|
603
|
+
.filter((name) => name.endsWith(".yml"))
|
|
604
|
+
.sort();
|
|
605
|
+
if (!taskFiles.length)
|
|
606
|
+
return {
|
|
607
|
+
exitCode: 1,
|
|
608
|
+
error: `No parity tasks found in ${relative(root, tasksDirectory).split(sep).join("/")}. Each task is one YAML file with schema, id, layers, critical_facts, and completion_markers.`,
|
|
609
|
+
};
|
|
610
|
+
const tasks = taskFiles.map((name) => {
|
|
611
|
+
const path = join(tasksDirectory, name);
|
|
612
|
+
try {
|
|
613
|
+
return materializeParityTask(parseParityTask(fs.read(path)), (source) => {
|
|
614
|
+
const absolute = join(root, source);
|
|
615
|
+
if (fs.pathType?.(absolute) !== "file")
|
|
616
|
+
throw new Error("source is not a file");
|
|
617
|
+
return fs.read(absolute);
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
throw new Error(`${name}: ${error.message}`, { cause: error });
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
const suite = runParitySuite(tasks, TASK_ASSEMBLER);
|
|
625
|
+
const serialized = serializeParitySuite(suite);
|
|
626
|
+
if (suite.failed)
|
|
627
|
+
return {
|
|
628
|
+
exitCode: 1,
|
|
629
|
+
output: serialized.trimEnd(),
|
|
630
|
+
error: `Task parity failed for ${suite.failed} of ${suite.total} task(s): critical facts or completion markers were lost by the ${TASK_ASSEMBLER.name} assembler. Fix context assembly or move the facts to a retrieved layer; do not delete the expectations.`,
|
|
631
|
+
};
|
|
632
|
+
return {
|
|
633
|
+
exitCode: 0,
|
|
634
|
+
output: serialized.trimEnd() +
|
|
635
|
+
`\nTask parity passed: every critical fact survived the ${TASK_ASSEMBLER.name} assembler.`,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
533
638
|
if (command === "scan") {
|
|
534
639
|
if (!fs.exists(join(aiw, "manifest.yml")))
|
|
535
640
|
return { exitCode: 1, error: "Run `aiw install` first." };
|
|
@@ -647,6 +752,28 @@ export async function runCommand(args, root, fs, services = {}) {
|
|
|
647
752
|
model: usage.model,
|
|
648
753
|
});
|
|
649
754
|
fs.write(usagePath, serializeTokenUsage(next));
|
|
755
|
+
const sidecarPath = join(aiw, USAGE_SIDECAR_PATH);
|
|
756
|
+
const ledger = fs.exists(sidecarPath) ? parseUsageSidecar(fs.read(sidecarPath)) : [];
|
|
757
|
+
let sidecar = ledger;
|
|
758
|
+
for (const usage of modelUsage) {
|
|
759
|
+
const measured = usage.detail.includes("(measured)");
|
|
760
|
+
const isInterpreter = usage.label === "interpreter" && measured && interpreterUsage;
|
|
761
|
+
sidecar = appendUsageSidecarEntry(sidecar, {
|
|
762
|
+
command: `scan.${usage.label}`,
|
|
763
|
+
label: measured ? "measured" : "estimated",
|
|
764
|
+
tiers: isInterpreter
|
|
765
|
+
? {
|
|
766
|
+
"cache-read": 0,
|
|
767
|
+
"fresh-input": interpreterUsage.promptTokens,
|
|
768
|
+
"cache-write": 0,
|
|
769
|
+
output: interpreterUsage.completionTokens,
|
|
770
|
+
}
|
|
771
|
+
: { "cache-read": 0, "fresh-input": usage.tokens, "cache-write": 0, output: 0 },
|
|
772
|
+
model: usage.model,
|
|
773
|
+
harness: "aiw-cli",
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
fs.write(sidecarPath, serializeUsageSidecar(sidecar));
|
|
650
777
|
}
|
|
651
778
|
return {
|
|
652
779
|
exitCode: 0,
|
|
@@ -1922,6 +2049,183 @@ export async function runCommand(args, root, fs, services = {}) {
|
|
|
1922
2049
|
fs.write(cachePath, serializeSummaryCache(createSummaryCache(context, summaryArg)));
|
|
1923
2050
|
return { exitCode: 0, output: summaryArg };
|
|
1924
2051
|
}
|
|
2052
|
+
if (command === "routing") {
|
|
2053
|
+
if (!fs.exists(join(aiw, "manifest.yml")))
|
|
2054
|
+
return { exitCode: 1, error: "Run `aiw install` first." };
|
|
2055
|
+
const usage = "Usage: aiw routing init | show | resolve --category=name";
|
|
2056
|
+
const action = args[1];
|
|
2057
|
+
const routingPath = join(aiw, ROUTING_TABLE_PATH);
|
|
2058
|
+
if (action === "init") {
|
|
2059
|
+
if (fs.exists(routingPath))
|
|
2060
|
+
return {
|
|
2061
|
+
exitCode: 1,
|
|
2062
|
+
error: `Routing table already exists: .aiw/${ROUTING_TABLE_PATH}. Edit it directly instead of re-initializing.`,
|
|
2063
|
+
};
|
|
2064
|
+
fs.write(routingPath, serializeRoutingTable(defaultRoutingTable()) +
|
|
2065
|
+
"# Add one rule per task category (dot-separated kebab-case, e.g. scan.interpreter).\n" +
|
|
2066
|
+
"# Name the evidence behind every rule — a benchmark, parity run, or ADR — before trusting it.\n" +
|
|
2067
|
+
"# Routing is advisory: humans can always override the resolved model.\n");
|
|
2068
|
+
return {
|
|
2069
|
+
exitCode: 0,
|
|
2070
|
+
output: `Routing table created: .aiw/${ROUTING_TABLE_PATH}. Add rules with named evidence, then resolve with \`aiw routing resolve --category=...\`.`,
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
if (action === "show") {
|
|
2074
|
+
if (!fs.exists(routingPath))
|
|
2075
|
+
return {
|
|
2076
|
+
exitCode: 1,
|
|
2077
|
+
error: `No routing table found. Run \`aiw routing init\` to create .aiw/${ROUTING_TABLE_PATH}.`,
|
|
2078
|
+
};
|
|
2079
|
+
return {
|
|
2080
|
+
exitCode: 0,
|
|
2081
|
+
output: serializeRoutingTable(parseRoutingTable(fs.read(routingPath))).trimEnd(),
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
if (action === "resolve") {
|
|
2085
|
+
const category = args.find((arg) => arg.startsWith("--category="))?.slice(11);
|
|
2086
|
+
if (!category)
|
|
2087
|
+
return { exitCode: 1, error: usage };
|
|
2088
|
+
if (!fs.exists(routingPath))
|
|
2089
|
+
return {
|
|
2090
|
+
exitCode: 1,
|
|
2091
|
+
error: `No routing table found. Run \`aiw routing init\` first.`,
|
|
2092
|
+
};
|
|
2093
|
+
let route;
|
|
2094
|
+
try {
|
|
2095
|
+
route = resolveRoute(parseRoutingTable(fs.read(routingPath)), category);
|
|
2096
|
+
}
|
|
2097
|
+
catch (error) {
|
|
2098
|
+
throw new Error(`Routing failed: ${error.message}`, { cause: error });
|
|
2099
|
+
}
|
|
2100
|
+
return {
|
|
2101
|
+
exitCode: 0,
|
|
2102
|
+
output: serializeResolvedRoute(route) +
|
|
2103
|
+
"\noverride: advisory only — a human may choose a different model at any time.",
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
return { exitCode: 1, error: usage };
|
|
2107
|
+
}
|
|
2108
|
+
if (command === "task-usage") {
|
|
2109
|
+
if (!fs.exists(join(aiw, "manifest.yml")))
|
|
2110
|
+
return { exitCode: 1, error: "Run `aiw install` first." };
|
|
2111
|
+
const parsed = parseNamedOptions(args.slice(1), ["workflow"]);
|
|
2112
|
+
if (!parsed)
|
|
2113
|
+
return { exitCode: 1, error: "Usage: aiw task-usage [--workflow=id-or-ticket]" };
|
|
2114
|
+
const sidecarPath = join(aiw, USAGE_SIDECAR_PATH);
|
|
2115
|
+
if (!fs.exists(sidecarPath))
|
|
2116
|
+
return {
|
|
2117
|
+
exitCode: 1,
|
|
2118
|
+
error: "No usage sidecar recorded yet. Correlate runs with `aiw usage-sidecar record --workflow=<id-or-ticket>` or run `aiw scan`.",
|
|
2119
|
+
};
|
|
2120
|
+
const sessionPath = join(aiw, "workflow.yml");
|
|
2121
|
+
const workflow = parsed.workflow ??
|
|
2122
|
+
(fs.exists(sessionPath)
|
|
2123
|
+
? (parseWorkflowSession(fs.read(sessionPath)).ticket ??
|
|
2124
|
+
parseWorkflowSession(fs.read(sessionPath)).workflowId)
|
|
2125
|
+
: undefined);
|
|
2126
|
+
if (!workflow)
|
|
2127
|
+
return {
|
|
2128
|
+
exitCode: 1,
|
|
2129
|
+
error: "No workflow specified and no active SDD task. Pass --workflow=<id-or-ticket>.",
|
|
2130
|
+
};
|
|
2131
|
+
const summary = summarizeTaskUsage(parseUsageSidecar(fs.read(sidecarPath)), workflow);
|
|
2132
|
+
return {
|
|
2133
|
+
exitCode: 0,
|
|
2134
|
+
output: serializeTaskUsage(summary),
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
if (command === "cost-report") {
|
|
2138
|
+
if (!fs.exists(join(aiw, "manifest.yml")))
|
|
2139
|
+
return { exitCode: 1, error: "Run `aiw install` first." };
|
|
2140
|
+
const sidecarPath = join(aiw, USAGE_SIDECAR_PATH);
|
|
2141
|
+
if (!fs.exists(sidecarPath))
|
|
2142
|
+
return {
|
|
2143
|
+
exitCode: 1,
|
|
2144
|
+
error: "No usage sidecar recorded yet. Record runs with `aiw usage-sidecar record` or run `aiw scan`.",
|
|
2145
|
+
};
|
|
2146
|
+
const tracePath = join(aiw, "generated/artifacts/traceability.yml");
|
|
2147
|
+
if (!fs.exists(tracePath))
|
|
2148
|
+
return {
|
|
2149
|
+
exitCode: 1,
|
|
2150
|
+
error: "Traceability graph is missing. Run `aiw trace` to build it from the approved specification and plan.",
|
|
2151
|
+
};
|
|
2152
|
+
const attribution = computeCostAttribution(parseUsageSidecar(fs.read(sidecarPath)), parseTraceabilityGraph(fs.read(tracePath)));
|
|
2153
|
+
return {
|
|
2154
|
+
exitCode: 0,
|
|
2155
|
+
output: serializeCostAttribution(attribution) +
|
|
2156
|
+
"\nSources: .aiw/checkpoints/usage-sidecar.yml + .aiw/generated/artifacts/traceability.yml (verified = linked evidence present).",
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
if (command === "usage-sidecar") {
|
|
2160
|
+
if (!fs.exists(join(aiw, "manifest.yml")))
|
|
2161
|
+
return { exitCode: 1, error: "Run `aiw install` first." };
|
|
2162
|
+
const usage = "Usage: aiw usage-sidecar record --command=name (--cache-read=N | --fresh-input=N | --cache-write=N | --output=N) [--model=name] [--harness=name] [--harness-version=text] [--estimated] | aiw usage-sidecar report";
|
|
2163
|
+
const action = args[1];
|
|
2164
|
+
const sidecarPath = join(aiw, USAGE_SIDECAR_PATH);
|
|
2165
|
+
if (action === "report") {
|
|
2166
|
+
const entries = fs.exists(sidecarPath) ? parseUsageSidecar(fs.read(sidecarPath)) : [];
|
|
2167
|
+
return {
|
|
2168
|
+
exitCode: 0,
|
|
2169
|
+
output: serializeUsageSidecarSummary(summarizeUsageSidecar(entries)) +
|
|
2170
|
+
(entries.length
|
|
2171
|
+
? `\nLedger: .aiw/${USAGE_SIDECAR_PATH} (${entries.length} entries, sequence-ordered, no timestamps)`
|
|
2172
|
+
: ""),
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
if (action === "record") {
|
|
2176
|
+
const recordArgs = args.slice(2).filter((arg) => arg !== "--estimated");
|
|
2177
|
+
if (recordArgs.some((arg) => arg !== "--estimated" && !arg.startsWith("--")))
|
|
2178
|
+
return { exitCode: 1, error: usage };
|
|
2179
|
+
const parsed = parseNamedOptions(recordArgs, [
|
|
2180
|
+
"command",
|
|
2181
|
+
"model",
|
|
2182
|
+
"harness",
|
|
2183
|
+
"harness-version",
|
|
2184
|
+
"requirement",
|
|
2185
|
+
"workflow",
|
|
2186
|
+
...USAGE_TIERS,
|
|
2187
|
+
]);
|
|
2188
|
+
if (!parsed?.command)
|
|
2189
|
+
return { exitCode: 1, error: usage };
|
|
2190
|
+
const tierValues = USAGE_TIERS.map((tier) => [tier, parsed[tier]]).filter(([, value]) => value !== undefined);
|
|
2191
|
+
if (!tierValues.length)
|
|
2192
|
+
return {
|
|
2193
|
+
exitCode: 1,
|
|
2194
|
+
error: "At least one token tier is required: --cache-read, --fresh-input, --cache-write, or --output.",
|
|
2195
|
+
};
|
|
2196
|
+
for (const [, value] of tierValues)
|
|
2197
|
+
if (!/^\d+$/.test(value))
|
|
2198
|
+
return { exitCode: 1, error: "Token tiers must be non-negative integers." };
|
|
2199
|
+
const entry = {
|
|
2200
|
+
command: parsed.command,
|
|
2201
|
+
label: (args.includes("--estimated") ? "estimated" : "measured"),
|
|
2202
|
+
tiers: Object.fromEntries(USAGE_TIERS.map((tier) => [tier, Number(parsed[tier] ?? 0)])),
|
|
2203
|
+
...(parsed.model ? { model: parsed.model } : {}),
|
|
2204
|
+
...(parsed.harness ? { harness: parsed.harness } : {}),
|
|
2205
|
+
...(parsed["harness-version"] ? { harnessVersion: parsed["harness-version"] } : {}),
|
|
2206
|
+
...(parsed.requirement ? { requirement: parsed.requirement } : {}),
|
|
2207
|
+
...(parsed.workflow ? { workflow: parsed.workflow } : {}),
|
|
2208
|
+
};
|
|
2209
|
+
let ledger;
|
|
2210
|
+
try {
|
|
2211
|
+
ledger = fs.exists(sidecarPath) ? parseUsageSidecar(fs.read(sidecarPath)) : [];
|
|
2212
|
+
}
|
|
2213
|
+
catch (error) {
|
|
2214
|
+
throw new Error(`Usage sidecar is unreadable: ${error.message}`, {
|
|
2215
|
+
cause: error,
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
const next = appendUsageSidecarEntry(ledger, entry);
|
|
2219
|
+
fs.write(sidecarPath, serializeUsageSidecar(next));
|
|
2220
|
+
return {
|
|
2221
|
+
exitCode: 0,
|
|
2222
|
+
output: `Usage recorded (seq ${next[next.length - 1].seq}, ${entry.label}): ${entry.command} — ${USAGE_TIERS.filter((tier) => entry.tiers[tier] > 0)
|
|
2223
|
+
.map((tier) => `${tier} ${entry.tiers[tier].toLocaleString("en-US")}`)
|
|
2224
|
+
.join(", ")}`,
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
return { exitCode: 1, error: usage };
|
|
2228
|
+
}
|
|
1925
2229
|
if (command === "token-usage") {
|
|
1926
2230
|
const usagePath = join(aiw, "usage.yml");
|
|
1927
2231
|
const entries = fs.exists(usagePath) ? parseTokenUsage(fs.read(usagePath)) : [];
|
package/docs/README.md
CHANGED
|
@@ -12,6 +12,8 @@ AI Workflow is a portable, spec-driven development system for AI coding agents.
|
|
|
12
12
|
- [Security and trust](security.md)
|
|
13
13
|
- [Quality and testing](quality.md)
|
|
14
14
|
- [Token efficiency](token-efficiency.md)
|
|
15
|
+
- [Token efficiency research](token-efficiency-research.md)
|
|
16
|
+
- [Token efficiency study notes](token-efficiency-study-notes.md)
|
|
15
17
|
- [Project intelligence](project-intelligence.md)
|
|
16
18
|
- [Semantic mapping](semantic-mapping.md)
|
|
17
19
|
- [Decision model evaluation](decision-model-evaluation.md)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Token Efficiency Research
|
|
2
|
+
|
|
3
|
+
Research notes and external references for the open problem: **guaranteeing low token consumption without sacrificing output quality**. This document consolidates published methodologies, open-source implementations, and a recommended path for AI Workflow.
|
|
4
|
+
|
|
5
|
+
> Methodology note: sources were collected via GitHub Search API and web search. Descriptions and savings figures come from the projects' own READMEs and blog posts and have not been independently validated. Last reviewed: October 2026.
|
|
6
|
+
|
|
7
|
+
## The core problem
|
|
8
|
+
|
|
9
|
+
Token optimization without a quality constraint is indistinguishable from degradation. Every credible source converges on the same inversion of the problem:
|
|
10
|
+
|
|
11
|
+
1. **Fix quality first** as a measurable constraint (benchmark of real work).
|
|
12
|
+
2. **Then minimize cost subject to that constraint.**
|
|
13
|
+
|
|
14
|
+
"Guaranteeing" low consumption is only possible once quality is pinned to a benchmark that an optimization cannot silently regress.
|
|
15
|
+
|
|
16
|
+
## Reference publications
|
|
17
|
+
|
|
18
|
+
| Publication | Key contribution | Relevance to AI Workflow |
|
|
19
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
20
|
+
| Uber Engineering — [Running a Software Factory Efficiently at Uber Scale](https://www.uber.com/blog/efficient-software-factory/) | Cost equation decomposed into six multiplicative, independently measurable terms; sequential driver decomposition with no unexplained residual; outcome-denominated cost (cost per merged PR, per triaged alert) | The measurement model to adopt: decompose, attribute, and denominate cost by unit of value |
|
|
21
|
+
| Anthropic — [Prompt Caching](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching) and [Token-Saving Updates](https://www.anthropic.com/news/token-saving-updates) | Cache reads at ~0.1x input price; TTL economics (5 min at 1.25x write, 1 h at 2x); thinking blocks cached in tool-use loops | Cache-friendly ordering of system prompt, rules, and project profile |
|
|
22
|
+
| Microsoft Azure — [The Economics of Agent Optimization](https://azure.microsoft.com/en-us/blog/the-economics-of-agent-optimization-context-engineering-for-enterprise-ai-agents/) | Context engineering framed as the primary economic lever for enterprise agents | Aligns with product principle 6 (just-in-time context) |
|
|
23
|
+
| LMSYS — [RouteLLM](https://www.lmsys.org/blog/2024-07-01-routellm/) | Formal cost/quality trade-off for model routing; 2x+ cost reduction with quality held | Routing strategy for the provider-neutral `AiProvider` interface |
|
|
24
|
+
| GetBeam — [AI Agent Token Cost Optimization](https://getbeam.dev/blog/ai-agent-token-cost-optimization.html) | Practical case study with spend decomposition | Cross-check on decomposition methodology |
|
|
25
|
+
|
|
26
|
+
### Common pattern across publications
|
|
27
|
+
|
|
28
|
+
Savings never come from a single technique. The pattern is:
|
|
29
|
+
|
|
30
|
+
1. Measure per task, not per aggregate.
|
|
31
|
+
2. Pin quality with a benchmark built from real work.
|
|
32
|
+
3. Optimize each cost term separately (price/token, tokens/request, requests/turn).
|
|
33
|
+
4. Hold the model constant when attributing savings (behavior shifts with every upgrade).
|
|
34
|
+
|
|
35
|
+
## Repositories by category
|
|
36
|
+
|
|
37
|
+
### Context engineering and token reduction
|
|
38
|
+
|
|
39
|
+
| Repository | Stars | Notes |
|
|
40
|
+
| ------------------------------------------------------------------------------------------------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| [microsoft/LLMLingua](https://github.com/microsoft/LLMLingua) | ~6.8k | Prompt compression (EMNLP'23, ACL'24); 5–20x compression. `gladehq/claude-shorthand` applies it to Claude Code (~55% claimed). Last-resort layer due to quality risk. |
|
|
42
|
+
| [ratel-ai/ratel](https://github.com/ratel-ai/ratel) | ~484 | Context engineering for agents: ~80% fewer tokens via fixing tool overload; in-process BM25 skills and memory. Architecturally the closest peer. |
|
|
43
|
+
| [jia-gao/leanctx](https://github.com/jia-gao/leanctx) | ~323 | Drop-in prompt compression, 40–60% claimed without app changes. |
|
|
44
|
+
| [rossoclt/context-guru](https://github.com/rossoclt/context-guru) | ~70 | Context optimization for coding agents: lower cost and latency "without sacrificing accuracy". |
|
|
45
|
+
| [pleasedodisturb/awesome-llm-token-optimization](https://github.com/pleasedodisturb/awesome-llm-token-optimization) | ~99 | **The reference map.** Quick-wins table (cache 90% input, routing 60–95%, batch 50%, compression 5–20x), papers, and tools by category. Read first. |
|
|
46
|
+
| [wai-21/llm-token-optimization](https://github.com/wai-21/llm-token-optimization) | ~2 | Decision Distillation, skill routing; 60–99% claimed. Unvalidated. |
|
|
47
|
+
| [8b-is/smart-tree](https://github.com/8b-is/smart-tree) | ~271 | Context-aware filesystem tooling replacing 20+ tools; token reduction via tool design. |
|
|
48
|
+
|
|
49
|
+
### Telemetry across harnesses
|
|
50
|
+
|
|
51
|
+
| Repository | Stars | Notes |
|
|
52
|
+
| --------------------------------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
53
|
+
| [pulsemcp/switchboard](https://github.com/pulsemcp/switchboard) | new | Self-hosted capture of agent transcripts (Claude Code, Codex, others). Cross-harness data source without vendor lock-in — the piece Uber gets from its gateway but a portable system must build. |
|
|
54
|
+
| [DeepAgentLabs/agenticlens](https://github.com/DeepAgentLabs/agenticlens) | ~58 | Open-source profiler: token usage, cost, latency, and optimization opportunities per session. |
|
|
55
|
+
| [traceloop/openllmetry](https://github.com/traceloop/openllmetry) | ~7.5k | GenAI observability on OpenTelemetry. The interoperable schema to adopt if telemetry grows beyond local use. |
|
|
56
|
+
| [gocenalper/agent-optimization](https://github.com/gocenalper/agent-optimization) | ~12 | Live token monitor for Claude Code / Codex: cost tracking, analysis, LLM-powered suggestions. |
|
|
57
|
+
| [BerriAI/litellm](https://github.com/BerriAI/litellm) | high | Proxy with routing and cost tracking for 100+ providers; candidate neutral measurement layer. |
|
|
58
|
+
| [timharris707/modeldeck](https://github.com/timharris707/modeldeck) | ~116 | Usage/quota tracking across Claude Code and Codex accounts. |
|
|
59
|
+
|
|
60
|
+
### Harnesses, workflows, and SDD
|
|
61
|
+
|
|
62
|
+
| Repository | Stars | Notes |
|
|
63
|
+
| ----------------------------------------------------------------------------------- | ----- | --------------------------------------------------------------------------------------------------------------- |
|
|
64
|
+
| [github/spec-kit](https://github.com/github/spec-kit) | ~142k | De-facto standard for spec-driven development; direct philosophical competitor. |
|
|
65
|
+
| [cisco-open/ai-harness-toolkit](https://github.com/cisco-open/ai-harness-toolkit) | ~15 | Skills for assessing AI-native engineering workflows in repositories. |
|
|
66
|
+
| [Zoria-Lind/dsh-token-optimizer](https://github.com/Zoria-Lind/dsh-token-optimizer) | ~20 | Layered pipeline: output ladder, MCP lazy loading, compaction driver. Small but architecturally interesting. |
|
|
67
|
+
| [ComposioHQ/composio](https://github.com/ComposioHQ/composio) | ~30k | 1000+ toolkits with tool search and context management — the "tool search" pattern Uber describes, productized. |
|
|
68
|
+
|
|
69
|
+
### Benchmarks with quality data (fix quality before optimizing)
|
|
70
|
+
|
|
71
|
+
| Repository | Stars | Notes |
|
|
72
|
+
| ------------------------------------------------------------------------------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| [HumphreySun98/repoagentbench](https://github.com/HumphreySun98/repoagentbench) | ~27 | Mines merged PRs into a local, contamination-free coding-agent benchmark. This is Uber's uReview methodology, open-sourced. |
|
|
74
|
+
| [mupt-ai/self-bench](https://github.com/mupt-ai/self-bench) | ~33 | Benchmarks agents on your own repository, Harbor-formatted evals. |
|
|
75
|
+
| [jettison-ai/jettison-parity](https://github.com/jettison-ai/jettison-parity) | new | Benchmark harness for context-optimization middleware: task parity, session cost, harness-agnostic. Defines exactly the experiment needed: same task, different context configuration, measure cost and quality. |
|
|
76
|
+
|
|
77
|
+
## Savings reference table
|
|
78
|
+
|
|
79
|
+
From the awesome-list and Uber's post, ordered by effort-to-savings ratio:
|
|
80
|
+
|
|
81
|
+
| Lever | Claimed savings | Quality risk | Applies at |
|
|
82
|
+
| -------------------------------------------- | ---------------------- | -------------------------- | ----------------------- |
|
|
83
|
+
| Prompt prefix caching | ~90% of cached input | None (transparent) | Provider layer |
|
|
84
|
+
| Tool schema lazy loading / tool search | 50–70K tokens/session | Low | Harness / MCP layer |
|
|
85
|
+
| Model routing (cheap model for simple tasks) | 60–95% on routed calls | Medium — needs benchmark | Provider / orchestrator |
|
|
86
|
+
| Batch APIs for non-urgent work | ~50% | None (same output) | Provider layer |
|
|
87
|
+
| Code-mode / batched tool calls | 55–100% per workflow | Low | Harness layer |
|
|
88
|
+
| Compaction budget | Context-dependent | Medium | Harness layer |
|
|
89
|
+
| Prompt compression (LLMLingua) | 5–20x | High — degrades edge cases | Last resort, middleware |
|
|
90
|
+
|
|
91
|
+
Combined pipelines claim 95–99% versus a naive baseline, but every high-risk row requires the quality gate below.
|
|
92
|
+
|
|
93
|
+
## Recommended path for AI Workflow
|
|
94
|
+
|
|
95
|
+
A four-step guarantee pipeline, reusing existing pieces:
|
|
96
|
+
|
|
97
|
+
### Step 1 — Fix quality with a self-benchmark
|
|
98
|
+
|
|
99
|
+
Mine real SDD tasks (specs, plans, verification results from `.aiw/checkpoints/` and `.aiw/generated/`) into an evaluable set, following the `repoagentbench` / `self-bench` pattern. Define the acceptance rule: **an optimization ships only if outcome does not fall below threshold X% on the benchmark.** AI Workflow already has the ingredients: `evidence.ts`, `traceability.ts`, quality gates, and `src/fixtures`.
|
|
100
|
+
|
|
101
|
+
### Step 2 — Decompose cost per term
|
|
102
|
+
|
|
103
|
+
Extend `token-usage.ts` toward Uber-style decomposition, per SDD stage and per skill:
|
|
104
|
+
|
|
105
|
+
- input vs output vs cached tokens per request;
|
|
106
|
+
- requests per turn and turns per task;
|
|
107
|
+
- cost per **verified requirement** (outcome-denominated), not per session.
|
|
108
|
+
|
|
109
|
+
Data sources: agent JSONL logs (Claude Code, Codex — the `switchboard` approach) plus AIW checkpoints. Deliver through the existing `dashboard.ts`, evolving it toward anti-pattern detection (Uber reports 16 distinct session anti-patterns; the traces to detect them are already captured).
|
|
110
|
+
|
|
111
|
+
### Step 3 — Apply levers in ROI order
|
|
112
|
+
|
|
113
|
+
1. Stable cacheable prefix: system prompt + confirmed profile + rules first; dynamic context last.
|
|
114
|
+
2. Routing: subagents and mechanical skills to cheaper models, manual override preserved (AIW's provider neutrality makes this a configuration, not a rewrite).
|
|
115
|
+
3. Tool overload: lazy loading / on-demand tool schemas for MCP surfaces.
|
|
116
|
+
4. Stage-budgeted compaction (already designed in `token-efficiency.md`).
|
|
117
|
+
5. Prompt compression only behind the quality gate.
|
|
118
|
+
|
|
119
|
+
### Step 4 — Continuous guardrail
|
|
120
|
+
|
|
121
|
+
A CI gate that reruns the self-benchmark on changes to models, skills, or configuration and reports Δcost and Δquality. This is the "model held constant" discipline from Uber: savings claims are only attributed when the model is fixed. Fail the gate when quality regresses or cost exceeds threshold.
|
|
122
|
+
|
|
123
|
+
### Suggested study order
|
|
124
|
+
|
|
125
|
+
1. `pleasedodisturb/awesome-llm-token-optimization` — full map.
|
|
126
|
+
2. `jettison-ai/jettison-parity` — how to design the cost/quality experiment.
|
|
127
|
+
3. `HumphreySun98/repoagentbench` — how to build the benchmark.
|
|
128
|
+
4. `pulsemcp/switchboard` and `DeepAgentLabs/agenticlens` — cross-harness telemetry layer.
|
|
129
|
+
|
|
130
|
+
## Gaps and open questions
|
|
131
|
+
|
|
132
|
+
- **Absolute cost data:** none of the sources publish per-task absolute costs in a form transferable to another codebase; only relative deltas. The self-benchmark must establish its own baseline.
|
|
133
|
+
- **Quality drift across model upgrades:** Uber acknowledges behavior shifts with every upgrade; savings measured against one model do not transfer. The CI guardrail (step 4) is the only mitigation found.
|
|
134
|
+
- **Compression safety:** no source provides a formal guarantee that prompt compression preserves behavior on edge cases; treat it as untrusted until benchmarked.
|
|
135
|
+
- **Telemetry privacy:** most third-party trackers are local-first, but AI Workflow's telemetry consent model (opt-in, minimal payload) should gate any integration with observability backends.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Token Efficiency Study Notes
|
|
2
|
+
|
|
3
|
+
Deep-dive notes from studying the five reference repositories recommended in [token-efficiency-research.md](token-efficiency-research.md). Local clones live outside the repository at `../token-research/` (sibling of this repo, not committed).
|
|
4
|
+
|
|
5
|
+
| Repository | Role in the plan | Maturity |
|
|
6
|
+
| -------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------- |
|
|
7
|
+
| `awesome-llm-token-optimization` | Complete map of levers, tools, and papers | Curated list; strong curation discipline |
|
|
8
|
+
| `jettison-ai/jettison-parity` | Cost/quality experiment design | Small but rigorous: offline, deterministic, 29 tests, honest-labeling rules |
|
|
9
|
+
| `HumphreySun98/repoagentbench` | How to build a benchmark from real work | Published on PyPI, real leaderboards (~$11 API spend for 12 runs) |
|
|
10
|
+
| `pulsemcp/switchboard` | Cross-harness telemetry architecture | Early (docs-first repo); architecture doc is the value |
|
|
11
|
+
| `DeepAgentLabs/agenticlens` | Evaluation, diagnosis, and model-fit scoring | Large active codebase (400+ files), versioned releases, research program |
|
|
12
|
+
|
|
13
|
+
## 1. awesome-llm-token-optimization — the map
|
|
14
|
+
|
|
15
|
+
Beyond the levers already captured in the research doc, the full read surfaced:
|
|
16
|
+
|
|
17
|
+
- **GitHub's own engineering result** ([github.blog](https://github.blog/ai-and-ml/github-copilot/improving-token-efficiency-in-github-agentic-workflows/)): pruning unused MCP tool schemas and swapping MCP calls for the `gh` CLI cut agentic-CI token spend **43–62%** across real workflows. This independently replicates Uber's CLI-resolved MCP lever.
|
|
18
|
+
- **Cockroach Labs**: re-sent context can be **62% of agent inference bills** — quantifies why per-turn context, not generation, is the dominant cost.
|
|
19
|
+
- **ProjectDiscovery**: raising cache hit rate from 7% to 84% across 9.8B cached tokens — the single largest documented saving, achieved purely by prompt-structure discipline (stable prefix).
|
|
20
|
+
- **Anthropic tool search**: 85% token reduction for large tool libraries; token-efficient tool use: 70% output reduction. Both are official provider features, not middleware.
|
|
21
|
+
- **Browser tool efficiency**: accessibility-tree approaches are 10–50x smaller than raw HTML; AI-summarized fetch ~20x better than Playwright MCP (1.5 KB vs 10–33 KB per page). Relevant to any web-driven skill.
|
|
22
|
+
- **Lossless rule-based compression achieves 3–4:1** with no model: strip prose/hedging, preserve numbers/entities/constraints/risks, dense bullets, self-contained 3–5K token sections. This is essentially AI Workflow's existing context strategy, externally validated.
|
|
23
|
+
- **Tokenizer warning**: newer Anthropic tokenizers produce ~30% more tokens for identical text at unchanged per-token prices — model upgrades can silently raise cost without any price change. Any cost attribution must be tokenizer-aware (Uber's "hold the model constant" rule exists for the same reason).
|
|
24
|
+
- **Router research caveat**: a 2026 game-theoretic analysis (arxiv:2602.09902) argues optimal routing is usually _static_ — dynamism mostly adds failure modes. Prefer a small fixed routing table per task category over clever per-request routing.
|
|
25
|
+
|
|
26
|
+
## 2. jettison-parity — the experiment design
|
|
27
|
+
|
|
28
|
+
The most directly transferable artifact. It answers cost and quality **together**, deterministically and offline.
|
|
29
|
+
|
|
30
|
+
### Four benchmark families
|
|
31
|
+
|
|
32
|
+
| Family | Measures | Transferable idea |
|
|
33
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| Standing context | Tokens per turn of persistent context (tool schemas, skill bodies, instruction files), per category, byte-reproducible | Measure AIW's rendered context (AGENTS.md/CLAUDE.md, skills, rules) the same way — before/after any resource change |
|
|
35
|
+
| Task parity | Same task run baseline vs optimized; scores completion, correct tool + params, **critical-fact retention**, rounds, tokens. **Exits nonzero on any regression** | The quality gate: `critical_facts` containment checks that facts which must survive compression actually survive |
|
|
36
|
+
| Session cost | Dollars of an N-turn session pricing cache-read / fresh-input / cache-write **separately**, in cache-hit and cache-miss regimes | Naive `tokens_saved × price` overstates savings ~10x on cache-heavy agents — AIW cost math must be tier-aware |
|
|
37
|
+
| Holdout RCT | Randomized trial over recorded request logs: sha256 arm assignment, mean tokens/request, 95% CI. Synthetic output is always labeled "synthetic demonstration — not evidence" | The standard for claiming a real-world saving once logs exist |
|
|
38
|
+
|
|
39
|
+
### Honest-measurement rules (adopt verbatim)
|
|
40
|
+
|
|
41
|
+
1. Every number carries a label: `measured` (exact tokenizer / provider price) vs `estimated`; labels propagate pessimistically.
|
|
42
|
+
2. Synthetic is never evidence.
|
|
43
|
+
3. Disclose the config alongside any number (content hash).
|
|
44
|
+
4. Cache-aware dollars (per billing tier).
|
|
45
|
+
5. No network, no clocks — byte-identical reruns.
|
|
46
|
+
|
|
47
|
+
### Adapter surface
|
|
48
|
+
|
|
49
|
+
A single method makes any optimizer scoreable: `optimize_request(body, provider) -> body`. AIW's equivalent boundary is the context assembly that produces a session's initial payload.
|
|
50
|
+
|
|
51
|
+
### Task file schema (quality pin)
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
"expected": {
|
|
55
|
+
"tool": "pg_describe_table",
|
|
56
|
+
"arguments": {"table": "orders"},
|
|
57
|
+
"critical_facts": ["Never run a migration", "db-staging.internal.example.com"],
|
|
58
|
+
"final_answer_contains": "DONE"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Six shipped scenarios cover: tool selection among 20 tools, search→load→call chains, numeric constraints, security rules, output-format requirements, mixed turns.
|
|
63
|
+
|
|
64
|
+
## 3. repoagentbench — building the benchmark
|
|
65
|
+
|
|
66
|
+
### Mining pipeline (PR → task)
|
|
67
|
+
|
|
68
|
+
1. PR title/body/base SHA/diff via `gh`.
|
|
69
|
+
2. Clone at base commit.
|
|
70
|
+
3. Split diff into `solution_tests.patch` + `solution_source.patch`; apply **tests only**, so the starting state is "post-PR tests vs pre-PR source".
|
|
71
|
+
4. Auto-generate `verify.sh` for the detected framework.
|
|
72
|
+
|
|
73
|
+
### Run protocol (the part to copy)
|
|
74
|
+
|
|
75
|
+
- Fresh workdir + per-task venv per run.
|
|
76
|
+
- `pre_verify` must **FAIL** (proves the task is broken), agent runs, `post_verify` must **PASS** (proves the fix).
|
|
77
|
+
- Artifacts: `diff.patch`, `agent.log`, `events.jsonl` — replayable and diffable.
|
|
78
|
+
- `mock-fix` oracle agent (applies the known PR diff) validates the harness itself: 3/3 pass.
|
|
79
|
+
|
|
80
|
+
### Findings that matter
|
|
81
|
+
|
|
82
|
+
- **The harness is part of the system under test**: same Anthropic model via aider (no-op fix, FAIL) vs native Claude Code (canonical fix, PASS) on the identical PR. Portability claims must be measured per-harness, not assumed.
|
|
83
|
+
- Contamination control: mine PRs post-dating the model's training cutoff (cites arxiv:2512.10218 — frontier models score 3x better on potentially-contaminated benchmarks).
|
|
84
|
+
- Cheap: ~$11 for 12 real-agent runs. A self-benchmark is not expensive.
|
|
85
|
+
|
|
86
|
+
**AIW translation**: our unit is not a PR but an SDD task — spec → plan → implementation → verification. The analog of "PR tests" is the AIW quality gate / verification evidence; the analog of `pre_verify` is "the requirement has no passing evidence yet".
|
|
87
|
+
|
|
88
|
+
## 4. switchboard — cross-harness telemetry architecture
|
|
89
|
+
|
|
90
|
+
Early-stage, docs-first. The architectural pattern is the takeaway:
|
|
91
|
+
|
|
92
|
+
- **Canonical format ("OpenTranscripts")**: every agent's transcript is converted by a deterministic, versioned, per-source converter into one schema. New agent → new converter; spec change → version bump.
|
|
93
|
+
- **One hard determinism boundary**: raw → canonical is deterministic ETL; only segmentation and outcome scoring are LLM-driven, downstream and optional (MVP makes zero LLM calls).
|
|
94
|
+
- **Hooks, not interception**: a small separately-distributed package per agent pipes sessions to user-owned storage (S3/GCS/local/HTTP behind one interface). The platform never owns the ingestion edge.
|
|
95
|
+
- Sidecar metadata per session: who ran it, trigger, model, token usage, harness + version. Deterministic rollups over the canonical table feed org-level dashboards.
|
|
96
|
+
|
|
97
|
+
**AIW translation**: AIW checkpoints are already a deterministic, versioned artifact per harness-agnostic run. The missing piece is per-session token/cost sidecar metadata recorded at checkpoint time (model, harness, tokens in/out/cached), which turns existing checkpoints into the `open_transcripts` equivalent without any new ingestion machinery.
|
|
98
|
+
|
|
99
|
+
## 5. agenticlens — evaluation and model-fit
|
|
100
|
+
|
|
101
|
+
Large active project (v0.10.0). Most relevant constructs:
|
|
102
|
+
|
|
103
|
+
- **Evaluator versioning**: every score carries its evaluator's version; evidence audits tie recommendations to the step they concern.
|
|
104
|
+
- **Experiments**: pass@k, pass^k, confidence intervals, significance tests, factor comparison — statistics done properly for agent evals.
|
|
105
|
+
- **Failure taxonomy + attribution**: deterministic diagnosis rules attribute failures to faulty step/agent, validated on controlled failures.
|
|
106
|
+
- **ModelFit**: task taxonomy, **hard constraints on quality/cost/latency/safety**, weighted scoring, per-category **Pareto frontiers with cost-per-success rankings**, recommendation confidence + explanations.
|
|
107
|
+
- **Advisory routing**: turns saved ModelFit evidence into explainable runtime decisions — advisory by default, execution requires explicit opt-in, with a local audit journal. Aligns with the static-routing finding from §1.
|
|
108
|
+
|
|
109
|
+
**AIW translation**: ModelFit's constraint-then-Pareto structure is exactly step 1–3 of our recommended path; "cost per success" is the outcome-denominated metric from the Uber post, productized.
|
|
110
|
+
|
|
111
|
+
## Synthesis: implementation blueprint for steps 1–2
|
|
112
|
+
|
|
113
|
+
Combining the five sources, the sequence with concrete designs:
|
|
114
|
+
|
|
115
|
+
1. **Standing-context benchmark** (jettison-parity, family 1): a command that renders AIW context for a fixture project and reports tokens per category (root context file, skills, rules, tool schemas) with a content hash. Deterministic, offline, zero API cost. First deliverable.
|
|
116
|
+
2. **Task parity suite** (jettison-parity, family 2 + repoagentbench protocol): SDD tasks from `src/fixtures` with `expected` blocks (required artifacts, `critical_facts` that must survive context filtering, completion markers). Baseline vs optimized context; **exit nonzero on regression**. `pre_verify must fail / post_verify must pass` discipline via existing quality gates.
|
|
117
|
+
3. **Checkpoint sidecar metadata** (switchboard pattern): record model, harness+version, token tiers (cache read / fresh input / cache write / output) into every `.aiw/checkpoints/` entry. Tier-aware, `measured` vs `estimated` labeled.
|
|
118
|
+
4. **Cost-per-verified-requirement** (Uber + agenticlens ModelFit): join step-3 metadata with `evidence.ts`/`traceability.ts` to report cost per unit of value, per SDD stage and per skill.
|
|
119
|
+
5. **Static routing table** (research consensus): a small per-task-category model matrix with hard quality constraints from step 2's benchmark; advisory with manual override, audit-logged.
|
|
120
|
+
6. **Holdout RCT** (jettison-parity, family 4): once real session logs accumulate, upgrade claims from "benchmark delta" to CI-backed RCT evidence.
|
|
121
|
+
|
|
122
|
+
### Guardrails to adopt wholesale
|
|
123
|
+
|
|
124
|
+
- Honest labeling (`measured`/`estimated`, pessimistic propagation).
|
|
125
|
+
- Synthetic results are never evidence.
|
|
126
|
+
- Config hash disclosed with every published number.
|
|
127
|
+
- Model (and tokenizer) held constant when attributing savings.
|
|
128
|
+
- Oracle baseline (mock-fix equivalent) to validate the harness itself.
|