|
| 1 | +# SDLBench Public Workflow and Prompt Cache Design |
| 2 | + |
| 3 | +**Status:** Approved design |
| 4 | +**Date:** 2026-08-30 |
| 5 | + |
| 6 | +## Problem |
| 7 | + |
| 8 | +SDLBench no longer represents the current SDL-MCP product path. The real-world benchmark calls internal handlers directly and encodes an older search-to-card-to-slice-to-skeleton workflow. This bypasses the public MCP catalog, current workflow guidance, compact response projection, and exact bytes that a model receives. |
| 9 | + |
| 10 | +The benchmark also treats input tokens as equally expensive. It cannot distinguish a stable prompt prefix that receives cache reads from a changing prefix that forces full-price input processing. This omission can hide the value of SDL-MCP's prompt-cache hygiene and can overstate products that reduce visible tokens while invalidating the reusable prefix. |
| 11 | + |
| 12 | +## Goals |
| 13 | + |
| 14 | +1. Exercise SDL-MCP and competing products through their public MCP surfaces. |
| 15 | +2. Capture the exact ordered tool catalog and model-facing transcript used by each benchmark run. |
| 16 | +3. Measure deterministic prompt-prefix reuse for every product. |
| 17 | +4. Import optional provider usage and calculate provider-reported cache efficiency. |
| 18 | +5. Preserve existing quality, coverage, and raw token measurements. |
| 19 | +6. Regenerate synthetic traces from the corrected benchmark contract. |
| 20 | + |
| 21 | +## Non-Goals |
| 22 | + |
| 23 | +- SDLBench does not call model-provider APIs or manage provider credentials. |
| 24 | +- SDLBench does not install the retired SDL workflow skill. |
| 25 | +- SDLBench does not infer cache usage from provider billing totals. |
| 26 | +- SDLBench does not give SDL-MCP product-specific scoring exceptions. |
| 27 | +- Provider cache metrics do not become CI gates until repeated runs establish variance bounds. |
| 28 | + |
| 29 | +## Architecture |
| 30 | + |
| 31 | +```text |
| 32 | +task fixture |
| 33 | + -> product adapter |
| 34 | + -> public MCP session |
| 35 | + -> exact tools/list and turn transcript |
| 36 | + -> benchmark result |
| 37 | + -> quality and coverage metrics |
| 38 | + -> raw token metrics |
| 39 | + -> deterministic cache simulation |
| 40 | + -> imported provider cache usage |
| 41 | +``` |
| 42 | + |
| 43 | +The shared runner owns task prompts, turn assembly, transcript serialization, token counting, cache calculations, result persistence, and cross-product aggregation. |
| 44 | + |
| 45 | +Each product adapter owns only the product boundary: |
| 46 | + |
| 47 | +- start or connect to the product's MCP server |
| 48 | +- return product identity and version |
| 49 | +- return the public ordered tool catalog |
| 50 | +- execute public tool calls |
| 51 | +- return the exact model-facing tool result |
| 52 | + |
| 53 | +SDL-MCP receives no private cache logic. Its workflow guidance enters the benchmark through the first public `tools/list` response, exactly as it enters an agent session. Repeated catalog loads must be byte-identical, and the guidance must appear only where the public server normally exposes it. |
| 54 | + |
| 55 | +The current internal-handler runner may remain temporarily as a diagnostic, but its results must be labeled legacy and excluded from claim-bearing comparisons. |
| 56 | + |
| 57 | +## Session Contract |
| 58 | + |
| 59 | +Every product run emits one canonical session transcript: |
| 60 | + |
| 61 | +```ts |
| 62 | +interface BenchmarkSession { |
| 63 | + productId: string; |
| 64 | + productVersion: string; |
| 65 | + taskId: string; |
| 66 | + runId: string; |
| 67 | + toolCatalog: unknown; |
| 68 | + turns: BenchmarkTurn[]; |
| 69 | +} |
| 70 | + |
| 71 | +interface BenchmarkTurn { |
| 72 | + turnIndex: number; |
| 73 | + request: unknown; |
| 74 | + toolCalls: unknown[]; |
| 75 | + toolResults: unknown[]; |
| 76 | +} |
| 77 | +``` |
| 78 | + |
| 79 | +The persisted representation uses deterministic JSON serialization. Tool order, object key order, and result bytes are part of the measured cache surface. Operational timestamps, durations, request IDs, absolute paths, and other non-actionable noise remain outside the model-facing transcript. |
| 80 | + |
| 81 | +The same task fixture defines the user request, turn limit, context budget, quality oracle, and completion criteria for every product. Product-specific instructions are allowed only when they arrive through that product's normal public installation or MCP loading path. |
| 82 | + |
| 83 | +## Cache Model |
| 84 | + |
| 85 | +Each benchmark result contains deterministic simulated metrics and optional provider metrics: |
| 86 | + |
| 87 | +```ts |
| 88 | +interface CacheResult { |
| 89 | + simulated: CacheMetrics; |
| 90 | + provider: |
| 91 | + | CacheMetrics |
| 92 | + | { status: "unavailable"; reason: string }; |
| 93 | +} |
| 94 | + |
| 95 | +interface CacheMetrics { |
| 96 | + totalInputTokens: number; |
| 97 | + uncachedInputTokens: number; |
| 98 | + cacheReadTokens: number; |
| 99 | + cacheWriteTokens: number; |
| 100 | + cacheHitPct: number; |
| 101 | + effectiveInputTokens: number; |
| 102 | + effectiveSavingsTokens: number; |
| 103 | + effectiveSavingsPct: number; |
| 104 | + cold: CachePhaseMetrics; |
| 105 | + warm: CachePhaseMetrics; |
| 106 | + turns: CacheTurnMetrics[]; |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +### Common formulas |
| 111 | + |
| 112 | +```text |
| 113 | +cacheHitPct = cacheReadTokens / totalInputTokens |
| 114 | +effectiveSavingsTokens = totalInputTokens - effectiveInputTokens |
| 115 | +effectiveSavingsPct = effectiveSavingsTokens / totalInputTokens |
| 116 | +``` |
| 117 | + |
| 118 | +Zero-input runs report zero percentages rather than `NaN`. |
| 119 | + |
| 120 | +### Deterministic simulation |
| 121 | + |
| 122 | +The simulator compares consecutive serialized model-input prefixes. A byte is reusable only while every preceding byte is unchanged. Content moved earlier, reordered keys, changing tool definitions, and injected runtime metadata invalidate the prefix from the first differing byte. |
| 123 | + |
| 124 | +Token counts use SDLBench's existing tokenizer. The simulator records: |
| 125 | + |
| 126 | +- tool catalog tokens |
| 127 | +- first-load workflow guidance tokens |
| 128 | +- tool-result tokens |
| 129 | +- reusable-prefix tokens |
| 130 | +- invalidated-prefix tokens |
| 131 | +- reusable-prefix percentage |
| 132 | + |
| 133 | +For simulation, reusable-prefix tokens are cache reads and all remaining input tokens are uncached. Cache writes are reported separately but use a neutral weight of `1.0`, so the simulated gate measures deterministic reuse rather than provider pricing. |
| 134 | + |
| 135 | +### Provider normalization |
| 136 | + |
| 137 | +Imported provider records are normalized before aggregation. |
| 138 | + |
| 139 | +For Anthropic-style usage, total input is the sum of uncached input, cache creation input, and cache read input. Cache creation input maps to writes; cache read input maps to reads. |
| 140 | + |
| 141 | +For OpenAI-style usage, total input is the reported input total, cached input comes from the cached-token detail, and uncached input is the difference. Cache writes remain zero when the provider does not expose them. |
| 142 | + |
| 143 | +The effective input calculation applies a versioned pricing profile: |
| 144 | + |
| 145 | +```text |
| 146 | +effectiveInputTokens = |
| 147 | + uncachedInputTokens |
| 148 | + + cacheReadTokens * cacheReadWeight |
| 149 | + + cacheWriteTokens * cacheWriteWeight |
| 150 | +``` |
| 151 | + |
| 152 | +Weights are the provider's read and write prices divided by its normal input price for the recorded model. This expresses cache cost in full-price input-token equivalents. Cold writes can therefore produce negative savings when a provider charges a write premium. Dollar savings use the same profile but remain informational. |
| 153 | + |
| 154 | +Provider comparisons are valid only when product results use the same provider, model, and pricing-profile version. |
| 155 | + |
| 156 | +## Provider Usage Import |
| 157 | + |
| 158 | +Provider usage is imported after a benchmark session. SDLBench never stores credentials or sends provider requests. |
| 159 | + |
| 160 | +```json |
| 161 | +{ |
| 162 | + "productId": "sdl-mcp", |
| 163 | + "taskId": "bug-fix-policy-window", |
| 164 | + "runId": "run-001", |
| 165 | + "provider": "anthropic", |
| 166 | + "model": "claude-sonnet-*", |
| 167 | + "pricingProfile": "anthropic-2026-08-30", |
| 168 | + "turns": [ |
| 169 | + { |
| 170 | + "turnIndex": 0, |
| 171 | + "inputTokens": 12000, |
| 172 | + "cacheReadTokens": 0, |
| 173 | + "cacheWriteTokens": 9000, |
| 174 | + "outputTokens": 800 |
| 175 | + } |
| 176 | + ] |
| 177 | +} |
| 178 | +``` |
| 179 | + |
| 180 | +The importer joins records by `productId`, `taskId`, `runId`, and `turnIndex`. It rejects duplicate turns, negative or non-finite values, unknown runs, mismatched product identities, and incompatible provider/model/profile combinations. |
| 181 | + |
| 182 | +Imported files must not contain raw prompts, tool results, API keys, provider request IDs, or user data. |
| 183 | + |
| 184 | +## Aggregation |
| 185 | + |
| 186 | +Whole-session amortized savings is the primary cache result. It includes cold cache writes and warm cache reads. Reports also retain cold and warm phase totals so readers can see startup cost and repeat-use benefit separately. |
| 187 | + |
| 188 | +Per product, SDLBench reports: |
| 189 | + |
| 190 | +- average, p25, p50, and minimum cache-hit percentage |
| 191 | +- average, p25, p50, and minimum effective-input savings percentage |
| 192 | +- total effective-input tokens and savings tokens |
| 193 | +- cold and warm breakdowns |
| 194 | +- number of ranked and unranked runs |
| 195 | + |
| 196 | +A product without provider telemetry remains in all normal SDLBench quality and raw-token reports. Its provider-cache status is `unavailable`, and it is excluded from provider-cache rankings rather than treated as zero savings. Deterministic simulated metrics remain required for every product. |
| 197 | + |
| 198 | +## Fairness Rules |
| 199 | + |
| 200 | +1. Use the same task, model, provider, turn limit, and context policy for compared products. |
| 201 | +2. Count each product's complete public tool catalog and normal workflow guidance. |
| 202 | +3. Capture exact model-facing results; do not count hidden server logs or internal envelopes. |
| 203 | +4. Reject provider-cache comparisons with mismatched provider, model, or pricing profile. |
| 204 | +5. Require both the token/cache result and the existing quality result; token savings cannot compensate for lost coverage or correctness. |
| 205 | + |
| 206 | +## Result Persistence |
| 207 | + |
| 208 | +The next SDLBench result schema adds these fields without deleting existing raw-token or quality fields: |
| 209 | + |
| 210 | +```ts |
| 211 | +interface ProductBenchmarkResult { |
| 212 | + product: { |
| 213 | + id: string; |
| 214 | + version: string; |
| 215 | + adapterVersion: string; |
| 216 | + }; |
| 217 | + session: BenchmarkSessionSummary; |
| 218 | + tokens: ExistingTokenMetrics; |
| 219 | + quality: ExistingQualityMetrics; |
| 220 | + cache: CacheResult; |
| 221 | +} |
| 222 | +``` |
| 223 | + |
| 224 | +Saved results include the benchmark schema version, tokenizer identity, fixture revision, pricing-profile identity, and transcript digest. Comparisons reject incompatible schema, tokenizer, or fixture revisions instead of silently combining them. |
| 225 | + |
| 226 | +Synthetic traces are regenerated from the same session/result contract. Existing stale traces remain historical artifacts and must not be presented as current SDLBench evidence. |
| 227 | + |
| 228 | +## CI Policy |
| 229 | + |
| 230 | +Initial CI hard-gates deterministic behavior only: |
| 231 | + |
| 232 | +- tool catalog and first-load workflow bytes remain stable |
| 233 | +- workflow guidance appears exactly once in the captured public catalog |
| 234 | +- simulated cache-hit and effective-savings metrics remain within approved tolerances |
| 235 | +- cleaned tool outputs do not regress coverage, precision, or recall |
| 236 | +- synthetic traces match the current schema and fixtures |
| 237 | + |
| 238 | +Provider metrics are report-only until enough repeated samples exist to set defensible variance bounds. Promoting a provider metric to a gate requires a separate reviewed change containing the sample evidence and chosen tolerance. |
| 239 | + |
| 240 | +## Failure Handling |
| 241 | + |
| 242 | +- Missing provider telemetry: preserve the run and mark provider cache unavailable. |
| 243 | +- Partial provider telemetry: reject the import for that run; do not estimate missing turns. |
| 244 | +- Catalog or transcript nondeterminism: fail deterministic CI and report the first differing turn or catalog location. |
| 245 | +- Adapter failure: fail that product run without substituting the legacy internal runner. |
| 246 | +- Quality regression: fail the existing quality gate even if cache metrics improve. |
| 247 | + |
| 248 | +## Rollout |
| 249 | + |
| 250 | +1. Add the shared public MCP session/transcript runner and adapt SDL-MCP first. |
| 251 | +2. Move the traditional baseline and each existing competitor onto the same adapter contract. |
| 252 | +3. Add deterministic prefix simulation and persisted cache metrics. |
| 253 | +4. Add provider-usage import, normalized reporting, and cache-ranking eligibility. |
| 254 | +5. Regenerate synthetic traces and update SDLBench methodology documentation. |
| 255 | + |
| 256 | +The rollout does not require provider credentials in CI. The first four steps can ship with provider telemetry absent and simulated cache metrics fully enforced. |
| 257 | + |
| 258 | +## Acceptance Criteria |
| 259 | + |
| 260 | +- SDLBench reaches SDL-MCP only through its public MCP protocol for claim-bearing runs. |
| 261 | +- The captured first tool catalog includes current SDL workflow guidance without an installed skill. |
| 262 | +- Repeated unchanged catalogs and tool results are byte-stable. |
| 263 | +- SDL-MCP, the traditional baseline, and every tested competitor produce the same session/result shape. |
| 264 | +- Every run saves simulated cache metrics; imported provider metrics are optional and explicitly ranked or unranked. |
| 265 | +- Whole-session, cold, warm, and per-turn cache values are reproducible from saved inputs. |
| 266 | +- Existing raw token, uncapped token, coverage, precision, and recall metrics remain available. |
| 267 | +- Synthetic traces are regenerated and no stale-trace warning remains. |
| 268 | +- SDLBench documentation explains cache formulas, fairness rules, ranking eligibility, and provider-profile compatibility. |
0 commit comments