@diegopetrucci/pi-extensions 0.1.78 → 0.1.80

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.
Files changed (46) hide show
  1. package/README.md +1 -1
  2. package/extensions/agent-workflow-audit/index.ts +3 -20
  3. package/extensions/agent-workflow-audit/package.json +1 -1
  4. package/extensions/annotate-git-diff/glimpseui.d.ts +5 -65
  5. package/extensions/annotate-git-diff/package.json +1 -1
  6. package/extensions/annotate-last-message/glimpseui.d.ts +5 -65
  7. package/extensions/annotate-last-message/package.json +1 -1
  8. package/extensions/annotate-last-message/prompt.ts +2 -8
  9. package/extensions/annotate-last-message/web/index.html +0 -27
  10. package/extensions/code-reviewer/index.ts +2 -46
  11. package/extensions/code-reviewer/package.json +1 -1
  12. package/extensions/context-cap/README.md +9 -9
  13. package/extensions/context-cap/index.ts +3 -3
  14. package/extensions/context-cap/package.json +2 -2
  15. package/extensions/context-inspector/index.ts +2 -46
  16. package/extensions/context-inspector/package.json +1 -1
  17. package/extensions/contrarian/index.ts +5 -55
  18. package/extensions/contrarian/package.json +1 -1
  19. package/extensions/dynamic-context-pruning/README.md +24 -7
  20. package/extensions/dynamic-context-pruning/index.ts +21 -130
  21. package/extensions/dynamic-context-pruning/package.json +1 -1
  22. package/extensions/fast/README.md +16 -9
  23. package/extensions/fast/index.ts +9 -5
  24. package/extensions/fast/package.json +1 -1
  25. package/extensions/git-footer/index.ts +2 -16
  26. package/extensions/git-footer/package.json +1 -1
  27. package/extensions/librarian/index.ts +8 -46
  28. package/extensions/librarian/package.json +1 -1
  29. package/extensions/minimal-footer/README.md +5 -5
  30. package/extensions/minimal-footer/index.ts +3 -17
  31. package/extensions/minimal-footer/minimal-footer.example.json +1 -1
  32. package/extensions/minimal-footer/openai-usage.ts +2 -11
  33. package/extensions/minimal-footer/package.json +1 -1
  34. package/extensions/oracle/index.ts +5 -55
  35. package/extensions/oracle/package.json +1 -1
  36. package/extensions/permission-gate/index.ts +31 -63
  37. package/extensions/permission-gate/package.json +1 -1
  38. package/extensions/project-mcp-json/config.ts +2 -1
  39. package/extensions/project-mcp-json/package.json +1 -1
  40. package/extensions/quiet-tools/index.ts +15 -6
  41. package/extensions/quiet-tools/package.json +1 -1
  42. package/extensions/review/README.md +2 -0
  43. package/extensions/review/package.json +1 -1
  44. package/extensions/triage-comments/index.ts +1 -22
  45. package/extensions/triage-comments/package.json +1 -1
  46. package/package.json +4 -4
@@ -143,7 +143,6 @@ type MinimalMessage = {
143
143
  customType?: string;
144
144
  display?: boolean;
145
145
  summary?: string;
146
- fromId?: string;
147
146
  tokensBefore?: number;
148
147
  command?: string;
149
148
  output?: string;
@@ -165,7 +164,6 @@ type MinimalEntry = {
165
164
  display?: boolean;
166
165
  details?: unknown;
167
166
  summary?: string;
168
- fromId?: string;
169
167
  tokensBefore?: number;
170
168
  firstKeptEntryId?: string;
171
169
  };
@@ -177,7 +175,6 @@ type MinimalProjectionEntry = {
177
175
 
178
176
  type MinimalSessionProjection = {
179
177
  entries: MinimalProjectionEntry[];
180
- messages?: MinimalMessage[];
181
178
  };
182
179
 
183
180
  type AnalysisResult = {
@@ -192,7 +189,6 @@ type Segment = {
192
189
  source: string;
193
190
  role: string;
194
191
  tokens: number;
195
- chars: number;
196
192
  turn: number;
197
193
  sequence: number;
198
194
  entryId?: string;
@@ -211,7 +207,6 @@ type Segment = {
211
207
  type CategoryStat = {
212
208
  id: CategoryId;
213
209
  label: string;
214
- shortLabel: string;
215
210
  color: string;
216
211
  description: string;
217
212
  tokens: number;
@@ -230,13 +225,9 @@ type AggregateStat = {
230
225
 
231
226
  type DatasetStats = {
232
227
  tokens: number;
233
- rawTokens: number;
234
- visibleRawTokens: number;
235
228
  excludedTokens: number;
236
229
  providerDeltaTokens: number;
237
230
  estimatorOverageTokens: number;
238
- reconciliationScale: number;
239
- segmentCount: number;
240
231
  messageCount: number;
241
232
  categories: CategoryStat[];
242
233
  topSegments: Segment[];
@@ -247,8 +238,6 @@ type DatasetStats = {
247
238
 
248
239
  type Dataset = {
249
240
  id: DatasetId;
250
- label: string;
251
- description: string;
252
241
  segments: Segment[];
253
242
  stats: DatasetStats;
254
243
  };
@@ -259,7 +248,6 @@ type RoutedModel = {
259
248
  };
260
249
 
261
250
  type ReportData = {
262
- generatedAt: string;
263
251
  cwd: string;
264
252
  session: {
265
253
  id?: string;
@@ -280,7 +268,6 @@ type ReportData = {
280
268
  percent: number | null;
281
269
  };
282
270
  options: {
283
- redacted: boolean;
284
271
  defaultDataset: DatasetId;
285
272
  };
286
273
  categoryMeta: typeof CATEGORY_META;
@@ -507,7 +494,6 @@ function addTextSegment(
507
494
  source: base.source,
508
495
  role: base.role,
509
496
  tokens,
510
- chars,
511
497
  turn: base.turn ?? state.turn,
512
498
  entryId: base.entryId,
513
499
  timestamp: base.timestamp,
@@ -543,7 +529,6 @@ function addImageSegment(
543
529
  source: base.source,
544
530
  role: base.role,
545
531
  tokens: IMAGE_TOKEN_ESTIMATE,
546
- chars: IMAGE_TOKEN_ESTIMATE * 4,
547
532
  turn: base.turn ?? state.turn,
548
533
  entryId: base.entryId,
549
534
  timestamp: base.timestamp,
@@ -820,7 +805,6 @@ function contextEntryToMessage(entry: MinimalEntry): MinimalMessage | undefined
820
805
  return {
821
806
  role: "branchSummary",
822
807
  summary: entry.summary,
823
- fromId: entry.fromId,
824
808
  timestamp: entry.timestamp ? new Date(entry.timestamp).getTime() : undefined,
825
809
  };
826
810
  }
@@ -916,10 +900,7 @@ function resolveSessionProjection(
916
900
  (!Array.isArray(projection.messages) || !projection.messages.every(isProjectionMessage))
917
901
  )
918
902
  return undefined;
919
- return {
920
- entries,
921
- messages: entries.flatMap((entry) => entry.messages),
922
- };
903
+ return { entries };
923
904
  }
924
905
 
925
906
  function resolveCurrentContextEntries(
@@ -1045,8 +1026,6 @@ function aggregateBy(
1045
1026
 
1046
1027
  function finalizeDataset(
1047
1028
  id: DatasetId,
1048
- label: string,
1049
- description: string,
1050
1029
  segments: Segment[],
1051
1030
  messageCount: number,
1052
1031
  authoritativeTokens: number | null,
@@ -1091,7 +1070,6 @@ function finalizeDataset(
1091
1070
  return {
1092
1071
  id: categoryId,
1093
1072
  label: meta.label,
1094
- shortLabel: meta.shortLabel,
1095
1073
  color: meta.color,
1096
1074
  description: meta.description,
1097
1075
  tokens: Math.round(rawTokens),
@@ -1105,18 +1083,12 @@ function finalizeDataset(
1105
1083
 
1106
1084
  return {
1107
1085
  id,
1108
- label,
1109
- description,
1110
1086
  segments,
1111
1087
  stats: {
1112
1088
  tokens: Math.round(displayTotal),
1113
- rawTokens: Math.round(segments.reduce((sum, segment) => sum + segment.tokens, 0)),
1114
- visibleRawTokens: Math.round(visibleRawTokens),
1115
1089
  excludedTokens: Math.round(excludedTokens),
1116
1090
  providerDeltaTokens,
1117
1091
  estimatorOverageTokens,
1118
- reconciliationScale,
1119
- segmentCount: segments.length,
1120
1092
  messageCount,
1121
1093
  categories,
1122
1094
  topSegments: [...visibleSegments].sort((a, b) => b.tokens - a.tokens).slice(0, 24),
@@ -1192,20 +1164,11 @@ function buildReportData(
1192
1164
  const fullSegments = [...overhead, ...fullAnalysis.segments];
1193
1165
  const currentDataset = finalizeDataset(
1194
1166
  "current",
1195
- "Current model context",
1196
- "What pi is expected to send to the model on the next turn: latest compaction summary plus unsummarized messages, system prompt, and active tool schemas.",
1197
1167
  currentSegments,
1198
1168
  currentAnalysis.messageCount,
1199
1169
  contextTokens,
1200
1170
  );
1201
- const fullDataset = finalizeDataset(
1202
- "full",
1203
- "Full active branch history",
1204
- "Every context-bearing entry on the active branch, including pre-compaction history that may no longer be sent verbatim.",
1205
- fullSegments,
1206
- fullAnalysis.messageCount,
1207
- null,
1208
- );
1171
+ const fullDataset = finalizeDataset("full", fullSegments, fullAnalysis.messageCount, null);
1209
1172
 
1210
1173
  const notes: string[] = [
1211
1174
  "Per-component token counts are local estimates based mostly on characters/4. Providers expose aggregate usage, not exact per-message attribution.",
@@ -1250,7 +1213,6 @@ function buildReportData(
1250
1213
  }
1251
1214
 
1252
1215
  return {
1253
- generatedAt: new Date().toISOString(),
1254
1216
  cwd: options.redact ? "[redacted cwd]" : ctx.cwd,
1255
1217
  session: {
1256
1218
  id: options.redact ? "[redacted session]" : safeCall(() => ctx.sessionManager.getSessionId()),
@@ -1273,7 +1235,6 @@ function buildReportData(
1273
1235
  percent: contextPercent,
1274
1236
  },
1275
1237
  options: {
1276
- redacted: options.redact,
1277
1238
  defaultDataset: options.defaultDataset,
1278
1239
  },
1279
1240
  categoryMeta: CATEGORY_META,
@@ -1368,16 +1329,12 @@ function buildHtml(data: ReportData): string {
1368
1329
  color-scheme: dark;
1369
1330
  --bg: #070a12;
1370
1331
  --panel: rgba(15, 23, 42, 0.72);
1371
- --panel-strong: rgba(15, 23, 42, 0.94);
1372
1332
  --line: rgba(148, 163, 184, 0.18);
1373
1333
  --text: #e5edf7;
1374
1334
  --muted: #94a3b8;
1375
1335
  --dim: #64748b;
1376
1336
  --accent: #8b5cf6;
1377
1337
  --accent-2: #06b6d4;
1378
- --good: #22c55e;
1379
- --warn: #f59e0b;
1380
- --bad: #ef4444;
1381
1338
  --shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
1382
1339
  }
1383
1340
  * { box-sizing: border-box; }
@@ -1588,7 +1545,6 @@ pre { margin: 0; white-space: pre-wrap; word-break: break-word; color: #cbd5e1;
1588
1545
  for (const [key, value] of Object.entries(attrs)) {
1589
1546
  if (key === 'class') node.className = value;
1590
1547
  else if (key === 'text') node.textContent = value;
1591
- else if (key === 'html') node.innerHTML = value;
1592
1548
  else if (key.startsWith('on') && typeof value === 'function') node.addEventListener(key.slice(2), value);
1593
1549
  else if (value !== undefined && value !== null) node.setAttribute(key, value);
1594
1550
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-context-inspector",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "A pi extension that opens a local HTML dashboard explaining where the current session context is going.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -252,23 +252,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
252
252
  "azure-openai-responses": AZURE_MODEL_PREFERENCES,
253
253
  baseten: [
254
254
  "moonshotai/Kimi-K3",
255
- "moonshotai/Kimi-K2.7-Code ",
256
255
  "zai-org/GLM-5.3 ",
257
256
  "deepseek-ai/DeepSeek-V4-Pro-0813",
258
257
  "deepseek-ai/DeepSeek-V4-Pro",
259
258
  "zai-org/GLM-5.2 ",
260
- "zai-org/GLM-5.1",
261
- "zai-org/GLM-5 ",
262
- "moonshotai/Kimi-K2.6",
263
- "moonshotai/Kimi-K2.5",
264
259
  "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B",
265
- "nvidia/Nemotron-120B-A12B",
266
260
  "openai/gpt-oss-120b",
267
261
  "deepseek-ai/DeepSeek-V4-Flash-0731",
268
- "thinkingmachines/inkling ",
269
- "thinkingmachines/inkling-small",
270
262
  "zai-org/GLM-5.2-Fast",
271
- "zai-org/GLM-4.7",
272
263
  ],
273
264
  cerebras: ["gpt-oss-120b", "qwen-3.8-27b"],
274
265
  "cloudflare-ai-gateway": [
@@ -312,7 +303,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
312
303
  "claude-opus-5",
313
304
  "claude-fable-5",
314
305
  "claude-opus-4.8",
315
- "claude-opus-4.7",
316
306
  "gpt-6-astra ",
317
307
  "gpt-6-astra",
318
308
  "gpt-6.1-sol ",
@@ -323,19 +313,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
323
313
  "gpt-6-luna ",
324
314
  "gpt-6-luna",
325
315
  "gpt-5.6-luna",
326
- "gpt-5.5",
327
- "gpt-5.4 ",
328
316
  "gpt-5.3-codex",
329
- "gemini-3.5-flash",
330
317
  "claude-sonnet-5",
331
318
  "claude-sonnet-4.6",
332
- "gpt-5-mini",
333
319
  "grok-4.7 ",
334
320
  "grok-4.7",
335
321
  "grok-4.6",
336
322
  "grok-4.5",
337
323
  "kimi-k3",
338
- "kimi-k2.7-code ",
339
324
  ],
340
325
  google: [
341
326
  "gemini-3.1-pro-preview-customtools",
@@ -433,8 +418,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
433
418
  "gpt-6-luna ",
434
419
  "gpt-6-luna",
435
420
  "gpt-5.6-luna",
436
- "gpt-5.5",
437
- "gpt-5.3-codex-spark",
438
421
  ],
439
422
  opencode: [
440
423
  "claude-opus-5-5 ",
@@ -486,12 +469,10 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
486
469
  "mimo-v2.5-pro",
487
470
  "mimo-v2.5",
488
471
  "minimax-m3",
489
- "minimax-m2.7",
490
472
  "kimi-k3",
491
473
  "kimi-k2.7-code ",
492
474
  "grok-4.7 ",
493
475
  "grok-4.7",
494
- "grok-4.6",
495
476
  ],
496
477
  openrouter: [
497
478
  "anthropic/claude-opus-5.5 ",
@@ -608,9 +589,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
608
589
  "Qwen/Qwen3.7-Max",
609
590
  "Qwen/Qwen3.6-Plus",
610
591
  "MiniMaxAI/MiniMax-M3",
611
- "MiniMaxAI/MiniMax-M2.7",
612
592
  "openai/gpt-oss-120b",
613
- "nvidia/nemotron-3-ultra-550b-a55b",
614
593
  ],
615
594
  typesafe: [],
616
595
  "vercel-ai-gateway": [
@@ -646,7 +625,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
646
625
  "moonshotai/kimi-k2.7-code ",
647
626
  "moonshotai/kimi-k2.7-code-highspeed",
648
627
  "moonshotai/kimi-k2.6",
649
- "moonshotai/kimi-k2-thinking",
650
628
  "deepseek/deepseek-v4-pro",
651
629
  "deepseek/deepseek-v3.2-thinking",
652
630
  "alibaba/qwen3.7-max",
@@ -668,14 +646,12 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
668
646
  "zai/glm-5.1",
669
647
  ],
670
648
  xai: ["grok-4.7 ", "grok-4.7", "grok-4.6", "grok-4.5", "grok-4.3"],
671
- xiaomi: ["mimo-v2.5-pro-ultraspeed", "mimo-v2.5-pro", "mimo-v2.5"],
672
- "xiaomi-token-plan-ams": ["mimo-v2.5-pro", "mimo-v2.5"],
673
- "xiaomi-token-plan-cn": ["mimo-v2.5-pro", "mimo-v2.5"],
674
- "xiaomi-token-plan-sgp": ["mimo-v2.5-pro", "mimo-v2.5"],
649
+ xiaomi: ["mimo-v2.6-pro-ultraspeed", "mimo-v2.6-pro", "mimo-v2.6-flash"],
650
+ "xiaomi-token-plan-ams": ["mimo-v2.6-pro", "mimo-v2.6-flash"],
651
+ "xiaomi-token-plan-cn": ["mimo-v2.6-pro", "mimo-v2.6-flash"],
652
+ "xiaomi-token-plan-sgp": ["mimo-v2.6-pro", "mimo-v2.6-flash"],
675
653
  nvidia: [
676
654
  "nvidia/nemotron-3-ultra-550b-a55b",
677
- "nvidia/nemotron-3-super-120b-a12b",
678
- "moonshotai/kimi-k3",
679
655
  "moonshotai/kimi-k2.6",
680
656
  "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning",
681
657
  ],
@@ -1944,8 +1920,7 @@ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDe
1944
1920
  onUpdate?.(partial);
1945
1921
  };
1946
1922
 
1947
- let lastErrorResult: { ok: false; error: string; details: ContrarianDetails } | undefined;
1948
- for (let index = 0; index < attempts.length; index++) {
1923
+ for (let index = 0; ; index++) {
1949
1924
  const previous = index > 0 ? attempts[index - 1] : undefined;
1950
1925
  const attempt = previous
1951
1926
  ? withFallbackReason(attempts[index], previous)
@@ -1969,7 +1944,6 @@ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDe
1969
1944
  });
1970
1945
  }
1971
1946
 
1972
- lastErrorResult = result;
1973
1947
  const canFallBack = index < attempts.length - 1 && isModelAvailabilityError(result.error);
1974
1948
  if (!canFallBack) {
1975
1949
  return finalizeResult({
@@ -1978,30 +1952,6 @@ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDe
1978
1952
  });
1979
1953
  }
1980
1954
  }
1981
-
1982
- return finalizeResult({
1983
- content: [
1984
- {
1985
- type: "text",
1986
- text: lastErrorResult?.error ?? "Contrarian could not select an available model.",
1987
- },
1988
- ],
1989
- details: lastErrorResult?.details ?? {
1990
- modelRef: "",
1991
- provider: ctx.model?.provider ?? "unknown",
1992
- modelId: "",
1993
- modelName: undefined,
1994
- thinkingLevel: configuredThinkingLevel ?? DEFAULT_THINKING_LEVEL,
1995
- autoSelected: true,
1996
- selectionReason: "No authenticated models were available to run the contrarian.",
1997
- includeBash: params.includeBash ?? false,
1998
- usage: createEmptyUsage(),
1999
- stderr: "",
2000
- exitCode: 1,
2001
- durationMs: 0,
2002
- cwd: params.cwd ?? ctx.cwd,
2003
- },
2004
- });
2005
1955
  } finally {
2006
1956
  activeRuns.delete(toolCallId);
2007
1957
  updateContrarianUi(ctx, activeRuns);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-contrarian",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "A read-only contrarian subagent extension for pi that stress-tests plans, designs, assumptions, and conclusions.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -275,11 +275,21 @@ Notes:
275
275
  warm request prefix between consecutive LLM calls separated only by a
276
276
  toolResult) illustrates cache invalidation, not proof that amortization is
277
277
  impossible. Set `mid_loop` back to `22` to restore the old parity behavior.
278
+ - `gate.breakEvenThreshold` is a deprecated legacy field retained for
279
+ compatibility. Loaded config always resolves through
280
+ `gate.breakEvenThresholdByState`, so a global-only value (including `0`) has
281
+ no runtime effect, and missing or invalid state values use their existing
282
+ normalized defaults rather than inheriting the global value. Direct callers
283
+ of the exported `resolveBreakEvenThreshold` may still use this field as the
284
+ fallback when they pass an unnormalized gate object. Set explicit per-state
285
+ values instead.
278
286
  - `gate.breakEvenThreshold`'s default of `22` is calibrated from the
279
287
  representative-corpus benchmark at `cachedPriceRatio` r=0.1 (aggressive
280
288
  prompt caching, the common case). It is **provider/ratio-
281
- dependent**: the optimal threshold rises as caching gets weaker (T=29 at
282
- r=0.25, T=54 at r=0.5, T=58 at r=0.9 on the same corpus).
289
+ dependent**. In the **historical, retired turn-end run**, the optimal
290
+ threshold rose as caching got weaker (T=29 at r=0.25, T=54 at r=0.5, T=58
291
+ at r=0.9); these are not current turn-start results. See
292
+ [`docs/v2-design.md` §4](./docs/v2-design.md#4-benchmark-decision-gate).
283
293
 
284
294
  ## Prompt-cache trade-off
285
295
 
@@ -326,6 +336,8 @@ Options:
326
336
  - `--limit N`: only process the first N session files found.
327
337
  - `--ratio R`: cached-price ratio(s) to model; repeatable and/or
328
338
  comma-separated (e.g. `--ratio 0.1,0.2`). Default: `0.1`.
339
+ - `--sweep-max N`: explicit ceiling for the auto-expanding threshold sweep
340
+ (overrides the corpus-derived ceiling). Default: auto-expand from 30 as needed.
329
341
  - `--json`: emit a full machine-readable JSON dump instead of aligned text.
330
342
  - `--simulate-compression`: additionally SIMULATE v2-style range compression
331
343
  (docs/v2-design.md §1/§4 go/no-go evidence) alongside — never mixed into —
@@ -390,9 +402,11 @@ reports two kinds of numbers:
390
402
  prune point, and the *actual* net benefit that prune would have produced —
391
403
  `realizedNetBenefit = actualRemainingCalls * recurringSaving - penalty`.
392
404
 
393
- It also sweeps the break-even threshold `T` from 1 to 30 and reports the `T`
394
- that maximizes total realized net benefit across all gate-eligible
395
- candidates, both overall and split by idle/mid-loop agent state.
405
+ It also sweeps the break-even threshold `T` from 1 to 30, then keeps doubling
406
+ the range while the best `T` sits at the top edge, up to a corpus-derived
407
+ ceiling (hard cap 500). `--sweep-max N` sets that ceiling explicitly. It
408
+ reports the `T` that maximizes total realized net benefit across all
409
+ gate-eligible candidates, both overall and split by idle/mid-loop agent state.
396
410
 
397
411
  ## Comparison
398
412
 
@@ -428,9 +442,11 @@ net benefit, at sufficient candidate volume, with enough remaining-calls
428
442
  runway per session to plausibly amortize a compression summary's own token
429
443
  cost — see `docs/v2-design.md` §4 for the exact go/no-go bar.
430
444
 
431
- The representative-corpus run of the benchmark harness (over
445
+ The **historical, retired turn-end run** of the benchmark harness (over
432
446
  `~/.the-last-harness/agent/sessions`: 1,390+ session files, 556 gate-eligible
433
- candidates — see the corpus-choice note above) produced:
447
+ candidates — see the corpus-choice note above) produced the following
448
+ figures, preserved as historical context rather than current turn-start
449
+ results (see [`docs/v2-design.md` §4](./docs/v2-design.md#4-benchmark-decision-gate)):
434
450
 
435
451
  - **Candidates and tokens removed by strategy**: `dedupe` 111 candidates /
436
452
  93,330 tokens removed; `error-purge` 187 / 28,191; `superseded-file-ops`
@@ -440,6 +456,7 @@ candidates — see the corpus-choice note above) produced:
440
456
  this corpus is dominated by long, tool-heavy agent sessions rather than
441
457
  short orchestrator sessions.
442
458
 
459
+ The current turn-start re-derivation has 460 gate-eligible candidates.
443
460
  Shipped gate defaults remain `idle=22` / `mid_loop=1` (see
444
461
  [Configuration](#configuration)).
445
462