@azure-id/orc 1.8.1 → 1.9.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +386 -0
  2. package/README-id.md +110 -73
  3. package/README.md +96 -33
  4. package/bin/cli.js +45520 -44867
  5. package/bin/graph-extract.js +2409 -120
  6. package/bin/graph-gain.js +404 -0
  7. package/bin/graph-map.js +232 -0
  8. package/bin/graph-notes.js +49 -8
  9. package/bin/graph-query.js +1770 -808
  10. package/bin/graph-resolve.js +93 -16
  11. package/bin/graph-shard.js +325 -0
  12. package/bin/graph.js +658 -605
  13. package/bin/verify-contracts.js +297 -56
  14. package/bin/verify-package.js +29 -1
  15. package/bin/webui/api.js +6 -0
  16. package/bin/webui/fixtures/index.js +6 -1
  17. package/bin/webui/fixtures/knowledge.js +41 -1
  18. package/bin/webui/fixtures/stats.js +107 -104
  19. package/bin/webui/i18n/en/knowledge.json +16 -1
  20. package/bin/webui/i18n/id/knowledge.json +16 -1
  21. package/bin/webui/js/panels/knowledge.js +68 -3
  22. package/mock-run/orc-quick.md +141 -113
  23. package/package.json +1 -1
  24. package/templates/agents/MODEL-MAPPING.md +15 -5
  25. package/templates/agents/orc-executor-haiku-4-5.md +25 -13
  26. package/templates/agents/orc-executor-opus-4-7-high.md +25 -13
  27. package/templates/agents/orc-executor-opus-4-7-med.md +25 -13
  28. package/templates/agents/orc-executor-opus-4-8-high.md +25 -13
  29. package/templates/agents/orc-executor-opus-5-high.md +25 -13
  30. package/templates/agents/orc-executor-opus-5-low.md +25 -13
  31. package/templates/agents/orc-executor-opus-5-med.md +25 -13
  32. package/templates/agents/orc-executor-sonnet-4-6-high.md +25 -13
  33. package/templates/agents/orc-executor-sonnet-4-6-med.md +25 -13
  34. package/templates/agents/orc-executor-sonnet-5-high.md +25 -13
  35. package/templates/agents/orc-graph-noter-sonnet-4-6-med.md +15 -12
  36. package/templates/agents/orc-planner-mini-opus-5-med.md +75 -69
  37. package/templates/agents/orc-planner-mini-sonnet-5-high.md +73 -67
  38. package/templates/agents/orc-recon-opus-5-low.md +99 -0
  39. package/templates/agents/orc-recon-sonnet-4-6-med.md +99 -0
  40. package/templates/commands/orc-mini.md +10 -12
  41. package/templates/commands/orc-quick.md +20 -33
  42. package/templates/hooks/README.md +13 -3
  43. package/templates/hooks/orc-graph-hook.js +148 -13
  44. package/templates/hooks/orc-trace.js +476 -471
  45. package/templates/skills/_shared/code-graph.md +148 -20
  46. package/templates/skills/_shared/phases/execution.md +13 -11
  47. package/templates/skills/_shared/phases/planning.md +8 -1
  48. package/templates/skills/_shared/phases/rules.md +172 -159
  49. package/templates/skills/_shared/phases/ship.md +5 -1
  50. package/templates/skills/_shared/phases/trace.md +4 -1
  51. package/templates/skills/_shared/phases/wiki-consult.md +10 -6
  52. package/templates/skills/_shared/read-ladder.md +10 -2
  53. package/templates/skills/_shared/return-validation.md +22 -0
  54. package/templates/skills/context-combiner/SKILL.md +13 -13
  55. package/templates/skills/orc/SKILL.md +1 -1
  56. package/templates/skills/orc/subskills/orc-execution/core.md +171 -159
  57. package/templates/skills/orc-analyze/SKILL.md +13 -13
  58. package/templates/skills/orc-diy/references/flow-schema.md +1 -1
  59. package/templates/skills/orc-mini/SKILL.md +148 -136
  60. package/templates/skills/orc-mini/examples/mini-run-mock.md +64 -50
  61. package/templates/skills/orc-mini/references/complexity.md +105 -0
  62. package/templates/skills/orc-quick/README.md +495 -423
  63. package/templates/skills/orc-quick/SKILL.md +157 -211
  64. package/templates/skills/orc-quick/references/context-doc.md +145 -114
  65. package/templates/skills/orc-quick/references/defect.md +101 -0
  66. package/templates/skills/orc-quick/references/dispatch-gate.md +55 -24
  67. package/templates/skills/orc-quick/references/gh-mode.md +148 -127
  68. package/templates/skills/orc-quick/references/look.md +107 -0
  69. package/templates/skills/orc-wiki/references/staleness.md +1 -1
@@ -21,7 +21,7 @@
21
21
  // case here; it is never an edit to a 1 700-line module.
22
22
  const { PROJECT, doctor, where } = require("./shell.js");
23
23
  const { config } = require("./settings.js");
24
- const { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph } = require("./knowledge.js");
24
+ const { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph, graphGain, graphGainMeasured } = require("./knowledge.js");
25
25
  const { runs, runDetail, runDetailClosed, aftermath } = require("./runs.js");
26
26
  const { stats, budgetForecast, budgetRates } = require("./stats.js");
27
27
  const { pact } = require("./pact.js");
@@ -309,6 +309,11 @@ module.exports.get = function get(route, q) {
309
309
  // v1.8.0 — the code graph, DRIFTED (the state the card must be designed for).
310
310
  case "/api/graph":
311
311
  return graph;
312
+ // v1.8.2 W4b — the gain meter, and the A/B behind its second button.
313
+ case "/api/graph/gain":
314
+ return graphGain;
315
+ case "/api/graph/gain/measured":
316
+ return graphGainMeasured;
312
317
  case "/api/patterns":
313
318
  return patterns;
314
319
  case "/api/gotchas":
@@ -313,4 +313,44 @@ const graph = {
313
313
  trace: "GRAPH-CONSULT drifted :: files=1920 symbols=5896 gen=42",
314
314
  };
315
315
 
316
- module.exports = { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph };
316
+ // v1.8.2 W4b — `orc graph gain --json`. The fixture carries the ESTIMATE word
317
+ // and a RANGE, because those are the two things the card must never lose.
318
+ const graphGain = {
319
+ ok: true,
320
+ state: "rows",
321
+ scope: "project",
322
+ run: null,
323
+ since: null,
324
+ calls_recorded: 41,
325
+ runs: 6,
326
+ generation_range: [38, 42],
327
+ paid: { card: 4120, source: 610, hints: 182, total: 4912 },
328
+ avoided: { low: 18900, high: 71400 },
329
+ calls: { low: 58, high: 113 },
330
+ net: { low: 13988, high: 66488 },
331
+ by_command: { ctx: 29, impact: 6, changes: 4, "for-slice": 2 },
332
+ hints: { injected: 11, read_notes: 3, updates: 9 },
333
+ estimate: true,
334
+ carry_note:
335
+ "a card is re-sent on every later turn of the agent that received it — and so is the read it replaced. Both halves carry the same multiplier, so these are FIRST-ENTRY tokens on both sides (eval/graph-replay.js).",
336
+ };
337
+
338
+ // The A/B, with enough runs in both groups to print a comparison.
339
+ const graphGainMeasured = {
340
+ ok: true,
341
+ state: "compared",
342
+ min_runs: 3,
343
+ corpus: { dir: "~/.claude/projects/-home-dev-shop", ok: true },
344
+ runs: { on: 6, off: 4 },
345
+ rows: { on: [], off: [] },
346
+ compare: {
347
+ exec_retrieval_calls: { on: { n: 6, median: 19, min: 12, max: 27 }, off: { n: 4, median: 31, min: 24, max: 39 }, delta_pct: -39, off_spread_pct: 48 },
348
+ exec_bash_searches: { on: { n: 6, median: 2, min: 0, max: 5 }, off: { n: 4, median: 3, min: 1, max: 6 }, delta_pct: -33, off_spread_pct: 167 },
349
+ exec_result_tokens: { on: { n: 6, median: 27900, min: 18200, max: 41800 }, off: { n: 4, median: 41200, min: 29100, max: 60300 }, delta_pct: -32, off_spread_pct: 76 },
350
+ session_result_tokens: { on: { n: 6, median: 188000, min: 141000, max: 244000 }, off: { n: 4, median: 187000, min: 132000, max: 251000 }, delta_pct: 1, off_spread_pct: 64 },
351
+ },
352
+ note:
353
+ "executor windows and session totals are kept apart: the main session is 74–80% of a run and the graph never touches it. Every delta is printed beside the OFF group's own spread — a delta smaller than that spread is noise, not a result.",
354
+ };
355
+
356
+ module.exports = { wiki, wikiDocs, wikiShow, wikiCoverage, wikiCoverageFull, wikiUnregistered, wikiPlan, wikiDebt, wikiUsage, patterns, patternShow, gotchas, gotchasArchived, gotchaPrunePreview, wikiImpact, graph, graphGain, graphGainMeasured };
@@ -1,104 +1,107 @@
1
- "use strict";
2
- /* fixtures/stats.js — canned data for `orc ui --fixtures`.
3
- Usage, the cost forecast and the dated rate table. The forecast is a RANGE
4
- with a sample count, and cache-read is its own segment.
5
-
6
- THE RULE FOR EVERY FILE IN HERE: carry ONE OF EVERY STATE, including the
7
- ugly ones. You cannot DESIGN a STALE chip on a fresh wiki, and a state
8
- with no fixture is a state nobody has ever looked at. A per-state count
9
- test asserts this, so a new state cannot ship without one.
10
-
11
- Shapes MUST match what `bin/cli.js --json` really emits — a drifted
12
- fixture is worse than no fixture. */
13
-
14
- const { PROJECT } = require("./shell.js");
15
-
16
- const stats = {
17
- log_dir: PROJECT + "/.claude/orc/logs",
18
- runs: 61,
19
- from: "2026-04-11",
20
- to: "2026-08-08",
21
- lanes: { orc: 22, quick: 17, mini: 9, wiki: 6, fast: 4, ultra: 2, grill: 1 },
22
- agents: { "orc-executor": 88, "orc-system-analyst": 21, "orc-planner": 21, "orc-reviewer": 18, "orc-verifier": 18, "orc-scout": 12, "orc-wiki-scanner": 9, "orc-trace-writer": 61 },
23
- dispatches: 248,
24
- downgrades: 3,
25
- unfinished: 2,
26
- unknown_lane: 1,
27
- };
28
-
29
- /* ============================================================ v0.46.0 ====== */
30
- /* Same rule as everything above: ONE OF EVERY STATE, including the ugly ones.
31
- You cannot design a BROKEN promise card on a ledger where everything holds,
32
- a REFUSE checklist on a repo with no refusals, or an empty-debt Knowledge
33
- panel on a wiki that owes four docs. test/webui.test.js asserts the count per
34
- state, so a new state cannot ship without a fixture. */
35
-
36
- const budgetForecast = {
37
- ok: true,
38
- plan: PROJECT + "/.claude/orc/run/store-credit/plan.md",
39
- tasks: 14,
40
- waves: 5,
41
- tokens: {
42
- p50: { input: 53400, cache_write: 362800, cache_read: 733800, output: 75100 },
43
- p90: { input: 81000, cache_write: 548000, cache_read: 1140000, output: 118000 },
44
- },
45
- raw: { p50: 1225100, p90: 1887000 },
46
- weighted: { p50: 564680, p90: 861000 },
47
- usd: { p50: 6.76, p90: 10.5 },
48
- price_table: { as_of: "2026-08-01", age_days: 9, stale: false, path: "bin/pricing.json" },
49
- quota: { available: true, plan: "max20", label: "Max 20x", window_pct: 18.2, weekly_pct: 4.1 },
50
- // A task at CONTEXT RISK — the state that cannot be designed on a small plan.
51
- context_risk: [{ task: "T12", agent: "orc-executor-opus-5-med", peak: 189000, window: 200000, pct: 95 }],
52
- bands: [
53
- { band: "[0,30)", agent: "orc-executor-haiku-4-5", count: 2, tasks: ["T01", "T02"], samples: 9, p50: { input: 1200, cache_write: 6400, cache_read: 11800, output: 900 }, p90: { input: 1800, cache_write: 9200, cache_read: 17000, output: 1400 } },
54
- { band: "[40,55)", agent: "orc-executor-sonnet-4-6-high", count: 3, tasks: ["T03", "T05", "T07"], samples: 12, p50: { input: 7800, cache_write: 52100, cache_read: 104000, output: 8400 }, p90: { input: 11000, cache_write: 75000, cache_read: 150000, output: 12000 } },
55
- { band: "[65,90)", agent: "orc-executor-opus-5-low", count: 1, tasks: ["T04"], samples: 6, p50: { input: 5700, cache_write: 40200, cache_read: 81000, output: 9300 }, p90: { input: 8100, cache_write: 58000, cache_read: 117000, output: 13400 } },
56
- // A band with insufficient history — the low-confidence warning is not
57
- // optional chrome, so the fixture has to be able to trigger it.
58
- { band: "[90,100]", agent: "orc-executor-opus-5-med", count: 1, tasks: ["T12"], samples: 2, p50: { input: 9100, cache_write: 61000, cache_read: 128000, output: 17000 }, p90: { input: 14000, cache_write: 94000, cache_read: 198000, output: 26000 } },
59
- ],
60
- fixed_roles: [
61
- { role: "orc-system-analyst-opus-5-high", samples: 11, p50: { input: 6100, cache_write: 41000, cache_read: 82000, output: 9000 }, p90: { input: 8600, cache_write: 58000, cache_read: 116000, output: 13000 } },
62
- { role: "orc-planner-opus-5-med", samples: 11, p50: { input: 5000, cache_write: 33000, cache_read: 68000, output: 7400 }, p90: { input: 7100, cache_write: 47000, cache_read: 96000, output: 10500 } },
63
- { role: "orc-trace-writer-haiku-4-5", samples: 41, p50: { input: 900, cache_write: 3000, cache_read: 5400, output: 700 }, p90: { input: 1300, cache_write: 4400, cache_read: 7800, output: 1000 } },
64
- ],
65
- low_confidence_bands: 1,
66
- min_samples: 5,
67
- // Non-zero on purpose: `unattributed` is shown whenever it is above 0, and a
68
- // fixture where it is always 0 makes that branch undesignable.
69
- unattributed: { blocks: 12, tokens: { input: 900, cache_write: 12000, cache_read: 24000, output: 1100 } },
70
- transcripts_readable: true,
71
- lanes: [
72
- { lane: "ultra", cmd: "/orc-ultra", raw: 2410000, weighted: 1090000, usd: 14.2, low_confidence_bands: 1, low_confidence_roles: 0 },
73
- { lane: "orc", cmd: "/orc", raw: 1225100, weighted: 564680, usd: 6.76, low_confidence_bands: 1, low_confidence_roles: 0 },
74
- { lane: "mini", cmd: "/orc-mini", raw: 410000, weighted: 188000, usd: 2.1, low_confidence_bands: 0, low_confidence_roles: 0 },
75
- { lane: "fast", cmd: "/orc-fast", raw: 0, weighted: 0, usd: null, low_confidence_bands: 1, low_confidence_roles: 1 },
76
- ],
77
- view: "auto",
78
- };
79
-
80
- const budgetRates = {
81
- ok: true,
82
- version: 1,
83
- calibrated_at: "10-08-2026 09:40:11",
84
- transcript_dir: "/home/you/.claude/projects/-example-project",
85
- transcripts_readable: true,
86
- transcript_files: 31,
87
- traces_read: 42,
88
- dispatches_joined: 96,
89
- price_table_as_of: "2026-08-01",
90
- bands: {
91
- "[0,30)": { samples: 9, p50: { input: 1200, cache_write: 6400, cache_read: 11800, output: 900 }, p90: { input: 1800, cache_write: 9200, cache_read: 17000, output: 1400 }, peak_p50: 24000, peak_p90: 38000 },
92
- "[40,55)": { samples: 12, p50: { input: 7800, cache_write: 52100, cache_read: 104000, output: 8400 }, p90: { input: 11000, cache_write: 75000, cache_read: 150000, output: 12000 }, peak_p50: 78000, peak_p90: 121000 },
93
- "[70,80)": { samples: 6, p50: { input: 5700, cache_write: 40200, cache_read: 81000, output: 9300 }, p90: { input: 8100, cache_write: 58000, cache_read: 117000, output: 13400 }, peak_p50: 96000, peak_p90: 142000 },
94
- "[90,100]": { samples: 2, p50: { input: 9100, cache_write: 61000, cache_read: 128000, output: 17000 }, p90: { input: 14000, cache_write: 94000, cache_read: 198000, output: 26000 }, peak_p50: 151000, peak_p90: 189000 },
95
- },
96
- roles: {
97
- "orc-system-analyst-opus-5-high": { samples: 11, p50: { input: 6100, cache_write: 41000, cache_read: 82000, output: 9000 }, p90: { input: 8600, cache_write: 58000, cache_read: 116000, output: 13000 }, peak_p50: 88000, peak_p90: 131000 },
98
- "orc-trace-writer-haiku-4-5": { samples: 41, p50: { input: 900, cache_write: 3000, cache_read: 5400, output: 700 }, p90: { input: 1300, cache_write: 4400, cache_read: 7800, output: 1000 }, peak_p50: 9000, peak_p90: 14000 },
99
- },
100
- lanes: {},
101
- unattributed: { blocks: 12, tokens: { input: 900, cache_write: 12000, cache_read: 24000, output: 1100 } },
102
- };
103
-
104
- module.exports = { stats, budgetForecast, budgetRates };
1
+ "use strict";
2
+ /* fixtures/stats.js — canned data for `orc ui --fixtures`.
3
+ Usage, the cost forecast and the dated rate table. The forecast is a RANGE
4
+ with a sample count, and cache-read is its own segment.
5
+
6
+ THE RULE FOR EVERY FILE IN HERE: carry ONE OF EVERY STATE, including the
7
+ ugly ones. You cannot DESIGN a STALE chip on a fresh wiki, and a state
8
+ with no fixture is a state nobody has ever looked at. A per-state count
9
+ test asserts this, so a new state cannot ship without one.
10
+
11
+ Shapes MUST match what `bin/cli.js --json` really emits — a drifted
12
+ fixture is worse than no fixture. */
13
+
14
+ const { PROJECT } = require("./shell.js");
15
+
16
+ const stats = {
17
+ log_dir: PROJECT + "/.claude/orc/logs",
18
+ runs: 61,
19
+ from: "2026-04-11",
20
+ to: "2026-08-08",
21
+ lanes: { orc: 22, quick: 17, mini: 9, wiki: 6, fast: 4, ultra: 2, grill: 1 },
22
+ agents: { "orc-executor": 88, "orc-system-analyst": 21, "orc-planner": 21, "orc-reviewer": 18, "orc-verifier": 18, "orc-scout": 12, "orc-wiki-scanner": 9, "orc-trace-writer": 61 },
23
+ dispatches: 248,
24
+ downgrades: 3,
25
+ unfinished: 2,
26
+ unknown_lane: 1,
27
+ // v1.8.2 W4b — the code-graph row. `paid` is exact; the avoided pair is an
28
+ // ESTIMATE and always a RANGE, which is why the panel never shows one number.
29
+ graph: { calls: 41, runs: 6, paid: 4912, avoided_low: 18900, avoided_high: 71400, estimate: true },
30
+ };
31
+
32
+ /* ============================================================ v0.46.0 ====== */
33
+ /* Same rule as everything above: ONE OF EVERY STATE, including the ugly ones.
34
+ You cannot design a BROKEN promise card on a ledger where everything holds,
35
+ a REFUSE checklist on a repo with no refusals, or an empty-debt Knowledge
36
+ panel on a wiki that owes four docs. test/webui.test.js asserts the count per
37
+ state, so a new state cannot ship without a fixture. */
38
+
39
+ const budgetForecast = {
40
+ ok: true,
41
+ plan: PROJECT + "/.claude/orc/run/store-credit/plan.md",
42
+ tasks: 14,
43
+ waves: 5,
44
+ tokens: {
45
+ p50: { input: 53400, cache_write: 362800, cache_read: 733800, output: 75100 },
46
+ p90: { input: 81000, cache_write: 548000, cache_read: 1140000, output: 118000 },
47
+ },
48
+ raw: { p50: 1225100, p90: 1887000 },
49
+ weighted: { p50: 564680, p90: 861000 },
50
+ usd: { p50: 6.76, p90: 10.5 },
51
+ price_table: { as_of: "2026-08-01", age_days: 9, stale: false, path: "bin/pricing.json" },
52
+ quota: { available: true, plan: "max20", label: "Max 20x", window_pct: 18.2, weekly_pct: 4.1 },
53
+ // A task at CONTEXT RISK — the state that cannot be designed on a small plan.
54
+ context_risk: [{ task: "T12", agent: "orc-executor-opus-5-med", peak: 189000, window: 200000, pct: 95 }],
55
+ bands: [
56
+ { band: "[0,30)", agent: "orc-executor-haiku-4-5", count: 2, tasks: ["T01", "T02"], samples: 9, p50: { input: 1200, cache_write: 6400, cache_read: 11800, output: 900 }, p90: { input: 1800, cache_write: 9200, cache_read: 17000, output: 1400 } },
57
+ { band: "[40,55)", agent: "orc-executor-sonnet-4-6-high", count: 3, tasks: ["T03", "T05", "T07"], samples: 12, p50: { input: 7800, cache_write: 52100, cache_read: 104000, output: 8400 }, p90: { input: 11000, cache_write: 75000, cache_read: 150000, output: 12000 } },
58
+ { band: "[65,90)", agent: "orc-executor-opus-5-low", count: 1, tasks: ["T04"], samples: 6, p50: { input: 5700, cache_write: 40200, cache_read: 81000, output: 9300 }, p90: { input: 8100, cache_write: 58000, cache_read: 117000, output: 13400 } },
59
+ // A band with insufficient history — the low-confidence warning is not
60
+ // optional chrome, so the fixture has to be able to trigger it.
61
+ { band: "[90,100]", agent: "orc-executor-opus-5-med", count: 1, tasks: ["T12"], samples: 2, p50: { input: 9100, cache_write: 61000, cache_read: 128000, output: 17000 }, p90: { input: 14000, cache_write: 94000, cache_read: 198000, output: 26000 } },
62
+ ],
63
+ fixed_roles: [
64
+ { role: "orc-system-analyst-opus-5-high", samples: 11, p50: { input: 6100, cache_write: 41000, cache_read: 82000, output: 9000 }, p90: { input: 8600, cache_write: 58000, cache_read: 116000, output: 13000 } },
65
+ { role: "orc-planner-opus-5-med", samples: 11, p50: { input: 5000, cache_write: 33000, cache_read: 68000, output: 7400 }, p90: { input: 7100, cache_write: 47000, cache_read: 96000, output: 10500 } },
66
+ { role: "orc-trace-writer-haiku-4-5", samples: 41, p50: { input: 900, cache_write: 3000, cache_read: 5400, output: 700 }, p90: { input: 1300, cache_write: 4400, cache_read: 7800, output: 1000 } },
67
+ ],
68
+ low_confidence_bands: 1,
69
+ min_samples: 5,
70
+ // Non-zero on purpose: `unattributed` is shown whenever it is above 0, and a
71
+ // fixture where it is always 0 makes that branch undesignable.
72
+ unattributed: { blocks: 12, tokens: { input: 900, cache_write: 12000, cache_read: 24000, output: 1100 } },
73
+ transcripts_readable: true,
74
+ lanes: [
75
+ { lane: "ultra", cmd: "/orc-ultra", raw: 2410000, weighted: 1090000, usd: 14.2, low_confidence_bands: 1, low_confidence_roles: 0 },
76
+ { lane: "orc", cmd: "/orc", raw: 1225100, weighted: 564680, usd: 6.76, low_confidence_bands: 1, low_confidence_roles: 0 },
77
+ { lane: "mini", cmd: "/orc-mini", raw: 410000, weighted: 188000, usd: 2.1, low_confidence_bands: 0, low_confidence_roles: 0 },
78
+ { lane: "fast", cmd: "/orc-fast", raw: 0, weighted: 0, usd: null, low_confidence_bands: 1, low_confidence_roles: 1 },
79
+ ],
80
+ view: "auto",
81
+ };
82
+
83
+ const budgetRates = {
84
+ ok: true,
85
+ version: 1,
86
+ calibrated_at: "10-08-2026 09:40:11",
87
+ transcript_dir: "/home/you/.claude/projects/-example-project",
88
+ transcripts_readable: true,
89
+ transcript_files: 31,
90
+ traces_read: 42,
91
+ dispatches_joined: 96,
92
+ price_table_as_of: "2026-08-01",
93
+ bands: {
94
+ "[0,30)": { samples: 9, p50: { input: 1200, cache_write: 6400, cache_read: 11800, output: 900 }, p90: { input: 1800, cache_write: 9200, cache_read: 17000, output: 1400 }, peak_p50: 24000, peak_p90: 38000 },
95
+ "[40,55)": { samples: 12, p50: { input: 7800, cache_write: 52100, cache_read: 104000, output: 8400 }, p90: { input: 11000, cache_write: 75000, cache_read: 150000, output: 12000 }, peak_p50: 78000, peak_p90: 121000 },
96
+ "[70,80)": { samples: 6, p50: { input: 5700, cache_write: 40200, cache_read: 81000, output: 9300 }, p90: { input: 8100, cache_write: 58000, cache_read: 117000, output: 13400 }, peak_p50: 96000, peak_p90: 142000 },
97
+ "[90,100]": { samples: 2, p50: { input: 9100, cache_write: 61000, cache_read: 128000, output: 17000 }, p90: { input: 14000, cache_write: 94000, cache_read: 198000, output: 26000 }, peak_p50: 151000, peak_p90: 189000 },
98
+ },
99
+ roles: {
100
+ "orc-system-analyst-opus-5-high": { samples: 11, p50: { input: 6100, cache_write: 41000, cache_read: 82000, output: 9000 }, p90: { input: 8600, cache_write: 58000, cache_read: 116000, output: 13000 }, peak_p50: 88000, peak_p90: 131000 },
101
+ "orc-trace-writer-haiku-4-5": { samples: 41, p50: { input: 900, cache_write: 3000, cache_read: 5400, output: 700 }, p90: { input: 1300, cache_write: 4400, cache_read: 7800, output: 1000 }, peak_p50: 9000, peak_p90: 14000 },
102
+ },
103
+ lanes: {},
104
+ unattributed: { blocks: 12, tokens: { input: 900, cache_write: 12000, cache_read: 24000, output: 1100 } },
105
+ };
106
+
107
+ module.exports = { stats, budgetForecast, budgetRates };
@@ -163,5 +163,20 @@
163
163
  "knowledge.wiki.unregistered": "The wiki is {state} — nothing can read it until it is registered.",
164
164
  "knowledge.wiki.why": "why: {reason}",
165
165
  "knowledge.graph.generation": "Generation",
166
- "knowledge.graph.selfheal": "It keeps itself current: a read repairs the files it is about to answer for, and the installed hook updates the map when a worker finishes. Nothing runs on a timer."
166
+ "knowledge.graph.selfheal": "It keeps itself current: a read repairs the files it is about to answer for, and the installed hook updates the map when a worker finishes. Nothing runs on a timer.",
167
+ "knowledge.gain.none": "No graph reads recorded yet. The first `orc graph ctx` starts the ledger.",
168
+ "knowledge.gain.strip": "paid {paid} · avoided ~{low}–{high} (estimate) · {calls} calls",
169
+ "knowledge.gain.paid": "Tokens put in (cards · source · hints)",
170
+ "knowledge.gain.avoided": "Retrieval probably kept out (estimate)",
171
+ "knowledge.gain.net": "Net tokens · net tool calls",
172
+ "knowledge.gain.byCommand": "By command",
173
+ "knowledge.gain.hints": "Hints (injected · read notes · updates)",
174
+ "knowledge.gain.measure": "Compare my ON and OFF runs",
175
+ "knowledge.gain.estimate": "`avoided` is an estimate of the retrieval a read ladder would have paid for, never a bill. `paid` is exact.",
176
+ "knowledge.gain.measuredHead": "{on} runs with the graph on vs {off} with it off",
177
+ "knowledge.gain.measuredFew": "Too few runs to compare. Three runs in each group are needed; the rows are recorded and the comparison waits.",
178
+ "knowledge.gain.execCalls": "worker retrieval",
179
+ "knowledge.gain.execTokens": "worker tool results",
180
+ "knowledge.gain.sessionTokens": "session tool results",
181
+ "knowledge.gain.noise": "a change smaller than the spread beside it is noise, not a result"
167
182
  }
@@ -163,5 +163,20 @@
163
163
  "knowledge.wiki.unregistered": "Wiki berstatus {state} — tidak ada yang bisa membacanya sampai terdaftar.",
164
164
  "knowledge.wiki.why": "sebab: {reason}",
165
165
  "knowledge.graph.generation": "Generasi",
166
- "knowledge.graph.selfheal": "Peta ini memperbarui dirinya: satu pembacaan memperbaiki berkas yang akan dijawabnya, dan hook yang terpasang memperbarui peta saat satu pekerja selesai. Tidak ada yang berjalan dengan pewaktu."
166
+ "knowledge.graph.selfheal": "Peta ini memperbarui dirinya: satu pembacaan memperbaiki berkas yang akan dijawabnya, dan hook yang terpasang memperbarui peta saat satu pekerja selesai. Tidak ada yang berjalan dengan pewaktu.",
167
+ "knowledge.gain.none": "Belum ada pembacaan grafik yang tercatat. `orc graph ctx` pertama memulai catatannya.",
168
+ "knowledge.gain.strip": "dibayar {paid} · dihindari ~{low}–{high} (perkiraan) · {calls} panggilan",
169
+ "knowledge.gain.paid": "Token yang dimasukkan (kartu · sumber · petunjuk)",
170
+ "knowledge.gain.avoided": "Pembacaan yang mungkin dihindari (perkiraan)",
171
+ "knowledge.gain.net": "Token bersih · panggilan alat bersih",
172
+ "knowledge.gain.byCommand": "Menurut perintah",
173
+ "knowledge.gain.hints": "Petunjuk (dikirim · catatan baca · pembaruan)",
174
+ "knowledge.gain.measure": "Bandingkan proses ON dan OFF saya",
175
+ "knowledge.gain.estimate": "`dihindari` adalah perkiraan pembacaan yang akan dibayar tangga baca, bukan tagihan. `dibayar` adalah angka pasti.",
176
+ "knowledge.gain.measuredHead": "{on} proses dengan grafik hidup vs {off} dengan grafik mati",
177
+ "knowledge.gain.measuredFew": "Terlalu sedikit proses untuk dibandingkan. Perlu tiga proses di tiap kelompok; barisnya tercatat dan perbandingannya menunggu.",
178
+ "knowledge.gain.execCalls": "pembacaan pekerja",
179
+ "knowledge.gain.execTokens": "hasil alat pekerja",
180
+ "knowledge.gain.sessionTokens": "hasil alat sesi",
181
+ "knowledge.gain.noise": "perubahan yang lebih kecil dari sebarannya adalah derau, bukan hasil"
167
182
  }
@@ -35,7 +35,7 @@ let KN_TAB = "wiki";
35
35
 
36
36
  async function renderKnowledge(body) {
37
37
  body.replaceChildren(skeleton(6));
38
- const [wikiRes, impactRes, patRes, gotRes, planRes, debtRes, usageRes, docsRes, covRes, refsRes, graphRes] = await Promise.all([
38
+ const [wikiRes, impactRes, patRes, gotRes, planRes, debtRes, usageRes, docsRes, covRes, refsRes, graphRes, gainRes] = await Promise.all([
39
39
  read("/api/wiki").catch((e) => ({ data: null, error: e })),
40
40
  read("/api/wiki/impact").catch((e) => ({ data: null, error: e })),
41
41
  read("/api/patterns").catch((e) => ({ data: null, error: e })),
@@ -50,6 +50,9 @@ async function renderKnowledge(body) {
50
50
  read("/api/wiki/refs").catch((e) => ({ data: null, error: e })),
51
51
  // v1.8.0 — the code graph. `graph status` exits 0-3 and every code is DATA.
52
52
  read("/api/graph").catch((e) => ({ data: null, error: e })),
53
+ // v1.8.2 W4b — the gain meter. Exit 1 (no ledger yet) is DATA, like every
54
+ // other read on this panel.
55
+ read("/api/graph/gain").catch((e) => ({ data: null, error: e })),
53
56
  ]);
54
57
  const d = {
55
58
  wiki: wikiRes.data || {},
@@ -63,6 +66,7 @@ async function renderKnowledge(body) {
63
66
  coverage: covRes.data,
64
67
  refs: refsRes.data,
65
68
  graph: graphRes.data,
69
+ graphGain: gainRes.data,
66
70
  // A read that FAILED is not the same as a read that came back empty, and
67
71
  // rendering them identically is what turned a truncated 30 KB payload into
68
72
  // "this repo has no wiki and no git" (v0.49.4). The server already puts the
@@ -145,7 +149,7 @@ function knowledgeHeaderStrip(d) {
145
149
  the state word is never replaced by a friendlier synonym. An update is FREE
146
150
  (parser only, no model), so it is a button; an OFF graph shows the one config
147
151
  command that turns it on, because turning a feature on is the user's call. */
148
- function graphCard(g, body) {
152
+ function graphCard(g, body, gain) {
149
153
  const c = card(t("knowledge.graph.title"));
150
154
  if (!g || !g.state) {
151
155
  c.append(empty(t("knowledge.graph.unknown"), t("knowledge.graph.unknownHint")));
@@ -191,15 +195,76 @@ function graphCard(g, body) {
191
195
  // click.
192
196
  c.append(el("div", "note", t("knowledge.graph.selfheal")));
193
197
  c.append(el("div", "note", t("knowledge.graph.locator")));
198
+ // K5 (v1.8.2 W4b) — the gain strip. It renders exactly the CLI's JSON and
199
+ // invents nothing: the word "estimate" is part of the string, never a
200
+ // tooltip, and `avoided` is a RANGE because one number for a counterfactual
201
+ // is the claim this whole meter refuses to make.
202
+ gainStrip(c, gain, body);
194
203
  return c;
195
204
  }
196
205
 
206
+ function gainStrip(c, g, body) {
207
+ if (!g || g.state !== "rows") {
208
+ c.append(el("div", "note", t("knowledge.gain.none")));
209
+ return;
210
+ }
211
+ c.append(
212
+ el(
213
+ "div",
214
+ "note",
215
+ t("knowledge.gain.strip")
216
+ .replace("{paid}", kTokUi(g.paid.total))
217
+ .replace("{low}", kTokUi(g.avoided.low))
218
+ .replace("{high}", kTokUi(g.avoided.high))
219
+ .replace("{calls}", String(g.calls_recorded))
220
+ )
221
+ );
222
+ c.append(
223
+ kvList([
224
+ [t("knowledge.gain.paid"), `${kTokUi(g.paid.total)} (${kTokUi(g.paid.card)} · ${kTokUi(g.paid.source)} · ${kTokUi(g.paid.hints)})`],
225
+ [t("knowledge.gain.avoided"), `~${kTokUi(g.avoided.low)} – ${kTokUi(g.avoided.high)}`],
226
+ [t("knowledge.gain.net"), `~${kTokUi(g.net.low)} – ${kTokUi(g.net.high)} · ~${g.calls.low} – ${g.calls.high}`],
227
+ [t("knowledge.gain.byCommand"), Object.entries(g.by_command).map(([k, v]) => `${k} ${v}`).join(" · ") || "—"],
228
+ [t("knowledge.gain.hints"), `${g.hints.injected} · ${g.hints.read_notes} · ${g.hints.updates}`],
229
+ ])
230
+ );
231
+ const btn = el("button", "btn btn-sm", t("knowledge.gain.measure"));
232
+ btn.type = "button";
233
+ const out = el("div", "note");
234
+ btn.addEventListener("click", async () => {
235
+ btn.disabled = true;
236
+ const r = await read("/api/graph/gain/measured").catch(() => ({ data: null }));
237
+ btn.disabled = false;
238
+ out.textContent = measuredText(r && r.data);
239
+ });
240
+ c.append(btn);
241
+ c.append(out);
242
+ c.append(el("div", "note", t("knowledge.gain.estimate")));
243
+ }
244
+
245
+ // The A/B in words. Every delta carries its N and the OFF group's own spread,
246
+ // because a delta smaller than that spread is noise and the panel must say so
247
+ // in the same sentence, never in a footnote.
248
+ function measuredText(m) {
249
+ if (!m || !m.compare) return t("knowledge.gain.measuredFew");
250
+ const c = m.compare;
251
+ const row = (label, x, unit) =>
252
+ `${label} ${x.off.median} → ${x.on.median} ${unit} (${x.delta_pct > 0 ? "+" : ""}${x.delta_pct}%, N ${x.off.n}/${x.on.n}, OFF spread ±${x.off_spread_pct}%)`;
253
+ return [
254
+ t("knowledge.gain.measuredHead").replace("{on}", String(m.runs.on)).replace("{off}", String(m.runs.off)),
255
+ row(t("knowledge.gain.execCalls"), c.exec_retrieval_calls, "calls"),
256
+ row(t("knowledge.gain.execTokens"), c.exec_result_tokens, "tokens"),
257
+ row(t("knowledge.gain.sessionTokens"), c.session_result_tokens, "tokens"),
258
+ t("knowledge.gain.noise"),
259
+ ].join(" · ");
260
+ }
261
+
197
262
  /* ── TAB 1 — WIKI ────────────────────────────────────────────────────────── */
198
263
  function knWikiTab(d, body) {
199
264
  const out = frag();
200
265
  out.append(wikiPlanCard(d.plan, d.debt, body));
201
266
  out.append(wikiOneDocCard(d.refs));
202
- out.append(graphCard(d.graph, body));
267
+ out.append(graphCard(d.graph, body, d.graphGain));
203
268
  if (d.usage && d.usage.rows) out.append(wikiUsageCard(d.usage, body));
204
269
 
205
270
  const w = d.wiki;