mixdog 0.9.4 → 0.9.5

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 (121) hide show
  1. package/README.md +112 -38
  2. package/package.json +4 -1
  3. package/scripts/bench/r5-orchestrated-task.json +7 -0
  4. package/scripts/bench/round-r13-clientmeta.json +464 -0
  5. package/scripts/bench/round-r14-betafeatures.json +466 -0
  6. package/scripts/bench/round-r15-fulldefault.json +462 -0
  7. package/scripts/bench/round-r16-sessionid.json +466 -0
  8. package/scripts/bench/round-r17-wirebytes.json +456 -0
  9. package/scripts/bench/round-r18-prewarm.json +468 -0
  10. package/scripts/bench/round-r19-clean.json +472 -0
  11. package/scripts/bench/round-r20-prewarm-clean.json +475 -0
  12. package/scripts/bench/round-r21-delta-retry.json +473 -0
  13. package/scripts/bench/round-r22-full-probe.json +693 -0
  14. package/scripts/bench/round-r23-itemprobe.json +701 -0
  15. package/scripts/bench/round-r24-shapefix.json +677 -0
  16. package/scripts/bench/round-r25-serial.json +464 -0
  17. package/scripts/bench/round-r26-parallel3.json +671 -0
  18. package/scripts/bench/round-r27-parallel10.json +894 -0
  19. package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
  20. package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
  21. package/scripts/bench/round-r30-instid.json +253 -0
  22. package/scripts/bench/round-r31-upgradeprobe.json +256 -0
  23. package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
  24. package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
  25. package/scripts/bench/round-r34-orchestrated.json +120 -0
  26. package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
  27. package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
  28. package/scripts/bench-run.mjs +40 -7
  29. package/scripts/internal-comms-bench.mjs +2 -4
  30. package/scripts/model-catalog-audit.mjs +209 -0
  31. package/scripts/model-list-sanitize-test.mjs +37 -0
  32. package/scripts/recall-bench.mjs +76 -13
  33. package/scripts/recall-quality-cases.json +12 -0
  34. package/scripts/tool-smoke.mjs +2 -2
  35. package/src/agents/heavy-worker/AGENT.md +7 -7
  36. package/src/agents/scheduler-task/AGENT.md +2 -3
  37. package/src/agents/webhook-handler/AGENT.md +2 -3
  38. package/src/agents/worker/AGENT.md +9 -10
  39. package/src/app.mjs +12 -1
  40. package/src/headless-role.mjs +7 -1
  41. package/src/mixdog-session-runtime.mjs +15 -14
  42. package/src/output-styles/default.md +14 -18
  43. package/src/output-styles/minimal.md +4 -5
  44. package/src/output-styles/simple.md +7 -8
  45. package/src/rules/agent/00-common.md +6 -5
  46. package/src/rules/agent/30-explorer.md +16 -5
  47. package/src/rules/lead/01-general.md +5 -5
  48. package/src/rules/lead/lead-brief.md +6 -3
  49. package/src/rules/lead/lead-tool.md +6 -4
  50. package/src/rules/shared/01-tool.md +17 -21
  51. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
  52. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +19 -0
  53. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +11 -3
  54. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +9 -1
  55. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +53 -9
  56. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +23 -10
  57. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +11 -9
  58. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +33 -4
  59. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
  60. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +93 -0
  61. package/src/runtime/agent/orchestrator/session/loop.mjs +27 -5
  62. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
  64. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -7
  65. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  66. package/src/runtime/channels/backends/discord-gateway.mjs +2 -2
  67. package/src/runtime/channels/backends/discord.mjs +15 -26
  68. package/src/runtime/channels/backends/telegram.mjs +8 -12
  69. package/src/runtime/channels/index.mjs +23 -43
  70. package/src/runtime/channels/lib/backend-dispatch.mjs +5 -3
  71. package/src/runtime/channels/lib/config.mjs +37 -149
  72. package/src/runtime/channels/lib/event-pipeline.mjs +4 -4
  73. package/src/runtime/channels/lib/event-queue.mjs +15 -9
  74. package/src/runtime/channels/lib/inbound-routing.mjs +2 -39
  75. package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
  76. package/src/runtime/channels/lib/scheduler.mjs +27 -113
  77. package/src/runtime/channels/lib/webhook/deliveries.mjs +3 -2
  78. package/src/runtime/channels/lib/webhook.mjs +8 -47
  79. package/src/runtime/memory/index.mjs +9 -0
  80. package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
  81. package/src/runtime/memory/lib/ko-morph.mjs +195 -0
  82. package/src/runtime/memory/lib/memory-recall-store.mjs +47 -10
  83. package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
  84. package/src/runtime/memory/lib/query-handlers.mjs +27 -5
  85. package/src/runtime/memory/tool-defs.mjs +2 -2
  86. package/src/runtime/shared/config.mjs +14 -4
  87. package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
  88. package/src/runtime/shared/schedules-store.mjs +13 -3
  89. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  90. package/src/session-runtime/config-helpers.mjs +45 -10
  91. package/src/session-runtime/quick-model-rows.mjs +46 -11
  92. package/src/session-runtime/quick-search-models.mjs +28 -27
  93. package/src/session-runtime/settings-api.mjs +34 -1
  94. package/src/session-runtime/tool-defs.mjs +1 -1
  95. package/src/session-runtime/workflow.mjs +1 -1
  96. package/src/standalone/agent-tool/tool-def.mjs +1 -1
  97. package/src/standalone/agent-tool.mjs +30 -0
  98. package/src/standalone/channel-admin.mjs +102 -90
  99. package/src/standalone/explore-tool.mjs +39 -10
  100. package/src/tui/App.jsx +126 -28
  101. package/src/tui/app/app-format.mjs +11 -4
  102. package/src/tui/app/channel-pickers.mjs +2 -4
  103. package/src/tui/app/maintenance-pickers.mjs +60 -28
  104. package/src/tui/app/model-options.mjs +5 -1
  105. package/src/tui/app/settings-picker.mjs +0 -1
  106. package/src/tui/app/transcript-window.mjs +8 -2
  107. package/src/tui/app/use-transcript-scroll.mjs +3 -1
  108. package/src/tui/app/use-transcript-window.mjs +19 -1
  109. package/src/tui/components/ConfirmBar.jsx +10 -7
  110. package/src/tui/components/Picker.jsx +32 -11
  111. package/src/tui/components/PromptInput.jsx +10 -1
  112. package/src/tui/components/StatusLine.jsx +6 -3
  113. package/src/tui/dist/index.mjs +440 -293
  114. package/src/tui/engine.mjs +48 -12
  115. package/src/tui/index.jsx +11 -52
  116. package/src/ui/statusline.mjs +10 -0
  117. package/src/workflows/default/WORKFLOW.md +15 -17
  118. package/src/workflows/sequential/WORKFLOW.md +15 -17
  119. package/vendor/ink/build/ink.js +9 -1
  120. package/scripts/recall-usecase-probe.json +0 -6
  121. package/src/runtime/channels/lib/holidays.mjs +0 -138
@@ -0,0 +1,254 @@
1
+ {
2
+ "round": "32",
3
+ "runner": "lead",
4
+ "opts": {
5
+ "provider": "openai-oauth",
6
+ "model": "gpt-5.5",
7
+ "effort": null,
8
+ "fast": false,
9
+ "workflow": null,
10
+ "env": {
11
+ "MIXDOG_DATA_DIR": "C:\\Users\\tempe\\.mixdog\\bench-data"
12
+ }
13
+ },
14
+ "tasks": 3,
15
+ "completed": 3,
16
+ "completion_rate": 100,
17
+ "sessions": [
18
+ "sess_24920_1783076179443_1783076179685_fbebe3e4d18c07e27c8a48fe8cd36028",
19
+ "sess_20680_1783076179459_1783076179699_20f64b7a9c29ae8b82bce96811957213",
20
+ "sess_3308_1783076179477_1783076179720_e7074a71c9936c9202b9c4095ddcaf89"
21
+ ],
22
+ "results": [
23
+ {
24
+ "id": "r4-debug-forwarding-gap",
25
+ "agent": "reviewer",
26
+ "ok": true,
27
+ "ms": 91356,
28
+ "sessionId": "sess_24920_1783076179443_1783076179685_fbebe3e4d18c07e27c8a48fe8cd36028",
29
+ "output": "sessionId: sess_24920_1783076179443_1783076179685_fbebe3e4d18c07e27c8a48fe8cd36028\n관련 경로를 기준으로 활성화부터 첫 포워딩까지의 흐름과 손실 지점을 확인하겠습니다.활성화 호출부와 inbound 바인딩 구간만 추가로 묶어서 확인하겠습니다.재영님, 조사 결과 bridge 활성화 직후의 지연은 일부 복구되지만, 기존 transcript에 이미 써진 출력은 실제 유실 가능성이 있습니다.\n\n- **최고 영향: 기존 transcript pre-bind 출력 유실** — `setContext()`가 기본/불일치 persisted 경로에서 cursor를 EOF로 둡니다; 이후 `forwardNewText()`가 과거 bytes를 보지 않습니다. `src/runtime/channels/lib/output-forwarder.mjs:117`, `src/runtime/channels/index.mjs:625`, `src/runtime/channels/index.mjs:1597` — backlog catch-up: **복구 안 됨**.\n\n- **binding 없음/잘못된 이전 binding** — 활성화는 `bridge-state.json`을 먼저 쓰고 ownership/binding을 뒤따라 수행합니다; 이 사이 현재 세션 transcript 발견이 실패하면 이전 path 또는 무binding 상태가 될 수 있습니다. `src/runtime/channels/lib/tool-dispatch.mjs:45`, `src/runtime/channels/index.mjs:577`, `src/runtime/channels/index.mjs:503` — backlog catch-up: **같은 persisted cursor일 때만 복구, 새 기존 파일은 유실**.\n\n- **not-yet-created transcript watch 실패** — `fs.watch()`는 없는 파일에서 실패하지만 pending re-arm poll이 파일 생성 후 watch 설치와 `forwardNewText()`를 호출합니다. `src/runtime/channels/index.mjs:340`, `src/runtime/channels/index.mjs:486`, `src/runtime/channels/lib/output-forwarder.mjs:647` — backlog catch-up: **60초 내 생성이면 복구, timeout 후에는 다음 rebind 전까지 지연/잠재 유실**.\n\n- **ownership/backend 연결 전 flush 무시** — `_isOwner()`가 false면 읽지도 않고 반환하므로 cursor는 보존됩니다; connect 후 `forwardNewText()` 또는 re-arm이 다시 실행됩니다. `src/runtime/channels/lib/output-forwarder.mjs:351`, `src/runtime/channels/index.mjs:758`, `src/runtime/channels/index.mjs:801` — backlog catch-up: **startup 지연은 복구, ownership을 잃으면 새 owner binding에 의존**.\n\n- **watch 설치 직전 write race** — `setContext()` stat 이후 `fs.watch()` 설치 사이 bytes는 즉시 `scheduleWatchFlush()`로 커버됩니다. `src/runtime/channels/lib/output-forwarder.mjs:665` — backlog catch-up: **복구됨**.\n\nVerdict: missing-file/ownership startup window는 대체로 지연 후 복구되지만, 이미 존재하는 새 transcript를 EOF baseline으로 bind하는 경로는 첫 assistant 출력을 조용히 유실할 수 있습니다.",
30
+ "card": {
31
+ "session": "sess_24920_1783076179443_1783076179685_fbebe3e4d18c07e27c8a48fe8cd36028",
32
+ "provider": "openai-oauth",
33
+ "model": "gpt-5.5",
34
+ "wall_ms": 80858,
35
+ "turns": 6,
36
+ "tool_calls": 7,
37
+ "tools_per_turn": 1.2,
38
+ "total_tool_ms": 1065,
39
+ "llm_stream_ms": 79802,
40
+ "cache_ratio": 0.6660624556860569,
41
+ "cached_tokens": 108032,
42
+ "prompt_tokens": 162195,
43
+ "uncached_tokens": 54163,
44
+ "output_tokens": 8103,
45
+ "cache_weighted_input_10": 64966.2,
46
+ "cache_weighted_input_25": 81171,
47
+ "prompt_growth": 38414,
48
+ "antipatterns": 1,
49
+ "issues": 10,
50
+ "issue_types": [
51
+ "cache",
52
+ "cache_break",
53
+ "broad_tool_result",
54
+ "service_tier",
55
+ "tool_churn_cluster"
56
+ ],
57
+ "read_fragmentation_paths": [],
58
+ "grep_sweep_count": 0
59
+ }
60
+ },
61
+ {
62
+ "id": "r4-impl-usage-summary",
63
+ "agent": "worker",
64
+ "ok": true,
65
+ "ms": 125526,
66
+ "sessionId": "sess_20680_1783076179459_1783076179699_20f64b7a9c29ae8b82bce96811957213",
67
+ "output": "sessionId: sess_20680_1783076179459_1783076179699_20f64b7a9c29ae8b82bce96811957213\n이미 일부 구현이 보이므로 현재 변경 상태를 확인한 뒤 필요한 보완만 하겠습니다.구현은 이미 작업 트리에 반영되어 있어 검증부터 진행하겠습니다.완료되었습니다. `usage:today` 스크립트와 `scripts/usage-today.mjs`가 추가되어 오늘 자정 이후 usage를 모델별/총합으로 집계합니다.\n\n- **Diff 요약**: `package.json`에 `usage:today` 추가, `scripts/usage-today.mjs` 신규 추가.\n- **검증**: `node --check scripts/usage-today.mjs` 통과, `npm run usage:today` 실행 완료.\n- **참고**: 기존 작업 트리에 unrelated 수정 파일 2개가 이미 있었습니다: `src/runtime/channels/lib/event-queue.mjs`, `src/runtime/channels/lib/output-forwarder.mjs`.",
68
+ "card": {
69
+ "session": "sess_20680_1783076179459_1783076179699_20f64b7a9c29ae8b82bce96811957213",
70
+ "provider": "openai-oauth",
71
+ "model": "gpt-5.5",
72
+ "wall_ms": 115026,
73
+ "turns": 14,
74
+ "tool_calls": 16,
75
+ "tools_per_turn": 1.1,
76
+ "total_tool_ms": 2818,
77
+ "llm_stream_ms": 112089,
78
+ "cache_ratio": 0.7775056333888508,
79
+ "cached_tokens": 238080,
80
+ "prompt_tokens": 306210,
81
+ "uncached_tokens": 68130,
82
+ "output_tokens": 7341,
83
+ "cache_weighted_input_10": 91938,
84
+ "cache_weighted_input_25": 127650,
85
+ "prompt_growth": 21818,
86
+ "antipatterns": 1,
87
+ "issues": 15,
88
+ "issue_types": [
89
+ "cache",
90
+ "cache_break",
91
+ "cache_miss",
92
+ "service_tier",
93
+ "missed_parallelism",
94
+ "tool_churn_cluster"
95
+ ],
96
+ "read_fragmentation_paths": [],
97
+ "grep_sweep_count": 0
98
+ }
99
+ },
100
+ {
101
+ "id": "r4-explain-compact-engine",
102
+ "agent": "explore",
103
+ "ok": true,
104
+ "ms": 71766,
105
+ "sessionId": "sess_3308_1783076179477_1783076179720_e7074a71c9936c9202b9c4095ddcaf89",
106
+ "output": "sessionId: sess_3308_1783076179477_1783076179720_e7074a71c9936c9202b9c4095ddcaf89\n재영님, compaction subsystem architecture brief입니다.\n\n- `compact.mjs` is the public facade; it re-exports `semanticCompactMessages` and `recallFastTrackCompactMessages` from `compact/engine.mjs`. `src/runtime/agent/orchestrator/session/compact.mjs:1`, `src/runtime/agent/orchestrator/session/compact.mjs:52`\n- Runtime pre-send auto path calls recall-fasttrack first when policy says so, otherwise semantic compaction. `src/runtime/agent/orchestrator/session/loop.mjs:329`, `src/runtime/agent/orchestrator/session/loop.mjs:414`, `src/runtime/agent/orchestrator/session/loop.mjs:439`\n- Manual/session paths enter via `runSessionCompaction`; clear-before-clear and explicit manual compact both call it. `src/runtime/agent/orchestrator/session/manager.mjs:1849`, `src/runtime/agent/orchestrator/session/manager.mjs:1950`\n- Auto fires when policy is enabled, a boundary exists, and pressure reaches trigger; reactive overflow forces it. `src/runtime/agent/orchestrator/session/loop/compact-policy.mjs:89`, `src/runtime/agent/orchestrator/session/loop/compact-policy.mjs:170`\n- Pressure is message tokens plus reserve; target budget is compact target plus reserve, capped by boundary. `src/runtime/agent/orchestrator/session/loop/compact-policy.mjs:151`, `src/runtime/agent/orchestrator/session/loop/compact-policy.mjs:163`\n- Manual compaction is forced; non-forced session compaction returns “below threshold” if pressure is under trigger. `src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs:146`, `src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs:176`\n- Semantic engine no-ops if sanitized transcript fits and not forced; otherwise it redacts, selects a window, prompts the provider, fits a summary, then emits system + summary + tail. `src/runtime/agent/orchestrator/session/compact/engine.mjs:286`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:312`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:314`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:367`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:384`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:392`\n- Window selection splits protected system context, previous summary, and live non-summary messages. `src/runtime/agent/orchestrator/session/compact/engine.mjs:100`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:102`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:110`\n- Semantic preserved tail keeps recent user turns within `preserveRecentBudget`; if newest turn alone is too large it keeps a fitting suffix. `src/runtime/agent/orchestrator/session/compact/engine.mjs:134`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:141`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:151`\n- If no user turns exist, semantic tail boundaries are any non-`tool` message, avoiding orphan tool results. `src/runtime/agent/orchestrator/session/compact/engine.mjs:121`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:193`\n- Recall-fasttrack preserves newest structured turns, default 2 users / 8k cap, dropping old turns then truncating if needed. `src/runtime/agent/orchestrator/session/compact/engine.mjs:449`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:467`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:601`\n- Recall-fasttrack accepts no-user tails and computes head as everything before the preserved tail. `src/runtime/agent/orchestrator/session/compact/engine.mjs:613`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:617`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:618`\n- Redaction hooks after no-op checks and before window selection, so preserved system/tail are measured and emitted redacted. `src/runtime/agent/orchestrator/session/compact/engine.mjs:286`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:306`, `src/runtime/agent/orchestrator/session/compact/engine.mjs:389`\n- Redaction is shape-preserving for verbatim tool-call arguments; prompt-side summaries also redact/truncate tool args. `src/runtime/agent/orchestrator/session/compact/text-utils.mjs:217`, `src/runtime/agent/orchestrator/session/compact/text-utils.mjs:261`, `src/runtime/agent/orchestrator/session/compact/summary.mjs:85`\n- Semantic failures in loop can fall back to unanchored tool-output prune unless recall-fasttrack already failed. `src/runtime/agent/orchestrator/session/loop.mjs:500`, `src/runtime/agent/orchestrator/session/loop.mjs:517`, `src/runtime/agent/orchestrator/session/loop.mjs:527`\n- Recall-fasttrack failure in session runner tries semantic degraded fallback; if still no compacted messages, failure metadata is recorded. `src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs:231`, `src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs:237`, `src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs:245`, `src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs:348`\n- Hard failures in the loop emit compact failure telemetry and throw `agentContextOverflowError`. `src/runtime/agent/orchestrator/session/loop.mjs:552`, `src/runtime/agent/orchestrator/session/loop.mjs:597`, `src/runtime/agent/orchestrator/session/loop.mjs:618`",
107
+ "card": {
108
+ "session": "sess_3308_1783076179477_1783076179720_e7074a71c9936c9202b9c4095ddcaf89",
109
+ "provider": "openai-oauth",
110
+ "model": "gpt-5.5",
111
+ "wall_ms": 61265,
112
+ "turns": 8,
113
+ "tool_calls": 11,
114
+ "tools_per_turn": 1.4,
115
+ "total_tool_ms": 1377,
116
+ "llm_stream_ms": 59937,
117
+ "cache_ratio": 0.6204795716575684,
118
+ "cached_tokens": 112640,
119
+ "prompt_tokens": 181537,
120
+ "uncached_tokens": 68897,
121
+ "output_tokens": 4296,
122
+ "cache_weighted_input_10": 80161,
123
+ "cache_weighted_input_25": 97057,
124
+ "prompt_growth": 33315,
125
+ "antipatterns": 0,
126
+ "issues": 11,
127
+ "issue_types": [
128
+ "cache",
129
+ "cache_break",
130
+ "cache_miss",
131
+ "service_tier"
132
+ ],
133
+ "read_fragmentation_paths": [],
134
+ "grep_sweep_count": 0
135
+ }
136
+ }
137
+ ],
138
+ "task_complete": true,
139
+ "task_errors": [],
140
+ "score_complete": true,
141
+ "score_errors": [],
142
+ "group": {
143
+ "n": 3,
144
+ "wall_ms": 85716.3,
145
+ "turns": 9.3,
146
+ "tool_calls": 11.3,
147
+ "tools_per_turn": 1.2,
148
+ "total_tool_ms": 1753.3,
149
+ "llm_stream_ms": 83942.7,
150
+ "cache_ratio": 0.7,
151
+ "cached_tokens": 152917.3,
152
+ "prompt_tokens": 216647.3,
153
+ "uncached_tokens": 63730,
154
+ "output_tokens": 6580,
155
+ "cache_weighted_input_10": 79021.7,
156
+ "cache_weighted_input_25": 101959.3,
157
+ "prompt_growth": 31182.3,
158
+ "antipatterns": 0.7,
159
+ "issues": 12,
160
+ "grep_sweep_count": 0
161
+ },
162
+ "cards": [
163
+ {
164
+ "session": "sess_24920_1783076179443_1783076179685_fbebe3e4d18c07e27c8a48fe8cd36028",
165
+ "provider": "openai-oauth",
166
+ "model": "gpt-5.5",
167
+ "wall_ms": 80858,
168
+ "turns": 6,
169
+ "tool_calls": 7,
170
+ "tools_per_turn": 1.2,
171
+ "total_tool_ms": 1065,
172
+ "llm_stream_ms": 79802,
173
+ "cache_ratio": 0.6660624556860569,
174
+ "cached_tokens": 108032,
175
+ "prompt_tokens": 162195,
176
+ "uncached_tokens": 54163,
177
+ "output_tokens": 8103,
178
+ "cache_weighted_input_10": 64966.2,
179
+ "cache_weighted_input_25": 81171,
180
+ "prompt_growth": 38414,
181
+ "antipatterns": 1,
182
+ "issues": 10,
183
+ "issue_types": [
184
+ "cache",
185
+ "cache_break",
186
+ "broad_tool_result",
187
+ "service_tier",
188
+ "tool_churn_cluster"
189
+ ],
190
+ "read_fragmentation_paths": [],
191
+ "grep_sweep_count": 0
192
+ },
193
+ {
194
+ "session": "sess_20680_1783076179459_1783076179699_20f64b7a9c29ae8b82bce96811957213",
195
+ "provider": "openai-oauth",
196
+ "model": "gpt-5.5",
197
+ "wall_ms": 115026,
198
+ "turns": 14,
199
+ "tool_calls": 16,
200
+ "tools_per_turn": 1.1,
201
+ "total_tool_ms": 2818,
202
+ "llm_stream_ms": 112089,
203
+ "cache_ratio": 0.7775056333888508,
204
+ "cached_tokens": 238080,
205
+ "prompt_tokens": 306210,
206
+ "uncached_tokens": 68130,
207
+ "output_tokens": 7341,
208
+ "cache_weighted_input_10": 91938,
209
+ "cache_weighted_input_25": 127650,
210
+ "prompt_growth": 21818,
211
+ "antipatterns": 1,
212
+ "issues": 15,
213
+ "issue_types": [
214
+ "cache",
215
+ "cache_break",
216
+ "cache_miss",
217
+ "service_tier",
218
+ "missed_parallelism",
219
+ "tool_churn_cluster"
220
+ ],
221
+ "read_fragmentation_paths": [],
222
+ "grep_sweep_count": 0
223
+ },
224
+ {
225
+ "session": "sess_3308_1783076179477_1783076179720_e7074a71c9936c9202b9c4095ddcaf89",
226
+ "provider": "openai-oauth",
227
+ "model": "gpt-5.5",
228
+ "wall_ms": 61265,
229
+ "turns": 8,
230
+ "tool_calls": 11,
231
+ "tools_per_turn": 1.4,
232
+ "total_tool_ms": 1377,
233
+ "llm_stream_ms": 59937,
234
+ "cache_ratio": 0.6204795716575684,
235
+ "cached_tokens": 112640,
236
+ "prompt_tokens": 181537,
237
+ "uncached_tokens": 68897,
238
+ "output_tokens": 4296,
239
+ "cache_weighted_input_10": 80161,
240
+ "cache_weighted_input_25": 97057,
241
+ "prompt_growth": 33315,
242
+ "antipatterns": 0,
243
+ "issues": 11,
244
+ "issue_types": [
245
+ "cache",
246
+ "cache_break",
247
+ "cache_miss",
248
+ "service_tier"
249
+ ],
250
+ "read_fragmentation_paths": [],
251
+ "grep_sweep_count": 0
252
+ }
253
+ ]
254
+ }
@@ -0,0 +1,115 @@
1
+ {
2
+ "round": "33",
3
+ "runner": "codex",
4
+ "opts": {
5
+ "provider": "openai-oauth",
6
+ "model": "gpt-5.5",
7
+ "effort": null,
8
+ "fast": false,
9
+ "workflow": null,
10
+ "env": {}
11
+ },
12
+ "tasks": 3,
13
+ "completed": 3,
14
+ "completion_rate": 100,
15
+ "sessions": [
16
+ "019f27a1-7dc4-7eb0-8195-f06a7c0022a3",
17
+ "019f27a1-7c61-7e70-bea3-392ebbe5e725",
18
+ "019f27a1-7df6-7690-aa6d-6734860ac723"
19
+ ],
20
+ "results": [
21
+ {
22
+ "id": "r4-debug-forwarding-gap",
23
+ "agent": "reviewer",
24
+ "ok": true,
25
+ "ms": 240320,
26
+ "sessionId": "019f27a1-7dc4-7eb0-8195-f06a7c0022a3",
27
+ "card": {
28
+ "session": "019f27a1-7dc4-7eb0-8195-f06a7c0022a3",
29
+ "wall_ms": 240320,
30
+ "uncached_in": 114654,
31
+ "cached_in": 981120,
32
+ "out_tokens": 14675,
33
+ "cache_ratio": 0.895,
34
+ "completed": 1
35
+ },
36
+ "output": "조사 결과, 활성화부터 첫 forward까지의 경로는 이렇게 이어집니다: `bridge-state.json` 기록 → ownership claim/refresh → backend connect → persisted/current transcript bind → `fs.watch` 또는 explicit `forwardNewText()` → transcript parse → backend send. 핵심 앵커는 [index.mjs:1735](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:1735), [index.mjs:1744](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:1744), [index.mjs:759](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:759), [index.mjs:625](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:625), [output-forwarder.mjs:351](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:351)입니다.\n\n**Findings**\n\n1. **High: 새 transcript가 이미 존재하지만 persisted path와 다르면 EOF로 바인딩되어 기존 출력이 조용히 스킵됩니다.** \n `bindPersistedTranscriptIfAny()`는 `catchUpFromPersisted: true`만 넘깁니다 [index.mjs:625](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:625). 기존 파일 발견 branch는 그대로 `applyTranscriptBinding()`을 호출합니다 [index.mjs:435](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:435), [index.mjs:441](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:441). 그런데 `setContext()`는 persisted transcript가 같은 파일일 때만 persisted offset을 쓰고, 아니면 `currentSize`로 cursor를 둡니다 [output-forwarder.mjs:117](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:117), [output-forwarder.mjs:122](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:122), [output-forwarder.mjs:125](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:125). \n Catch-up: **No**, 단 persisted path가 같은 경우만 recovered.\n\n2. **High: activation rebind 때 session record/transcript를 못 찾으면 binding 자체가 없고, 이후 일반 inbound bind가 과거 출력을 EOF 처리할 수 있습니다.** \n rebind는 30회 poll 후 pending path도 previous path도 없으면 빈 문자열로 종료합니다 [index.mjs:431](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:431), [index.mjs:515](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:515). post-connect도 `forwarder.transcriptPath`가 없으면 실질적으로 읽을 파일이 없습니다 [index.mjs:801](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:801), [output-forwarder.mjs:150](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:150). 나중에 inbound가 이미 존재하는 transcript를 잡으면 `applyTranscriptBinding()`은 catch-up 옵션 없이 호출되고 [index.mjs:1597](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:1597), `setContext()` 기본값은 EOF입니다 [output-forwarder.mjs:125](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:125). \n Catch-up: **No automatic catch-up**.\n\n3. **Medium: transcript path는 알지만 파일이 아직 없으면 `fs.watch`는 실패하고 re-arm poll로 지연 복구됩니다.** \n `startWatch()`는 없는 파일에서 catch 후 watcher를 닫습니다 [output-forwarder.mjs:656](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:656), [output-forwarder.mjs:667](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:667). pending branch는 `replayFromStart` 또는 persisted cursor를 설정한 뒤 [index.mjs:485](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:485), [index.mjs:486](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:486), 250ms poll을 60초까지 돌려 파일 생성 시 `startWatch()`와 `forwardNewText()`를 호출합니다 [index.mjs:348](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:348), [index.mjs:349](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:349), [index.mjs:376](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:376). \n Catch-up: **Yes within 60s**, 그 이후는 자동 복구 없음.\n\n4. **Medium: bridge-state는 backend/owner 준비 전에 active가 되므로 그 사이 forward 시도는 fail-closed로 지연됩니다.** \n state write가 먼저 일어나고 [index.mjs:1735](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:1735), [index.mjs:1736](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:1736), binding task는 `backend.connect()` 전에 시작됩니다 [index.mjs:759](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:759), [index.mjs:769](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:769). 이때 owner probe는 `bridgeRuntimeConnected && currentOwnerState().owned`라 false입니다 [index.mjs:316](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:316), 그래서 `forwardNewText()`/`forwardFinalText()`는 읽지 않고 반환합니다 [output-forwarder.mjs:351](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:351), [output-forwarder.mjs:491](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:491). connect 후에는 existing file이면 `forwardNewText()`, pending file이면 re-arm을 다시 겁니다 [index.mjs:803](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:803), [index.mjs:807](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:807), [index.mjs:809](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:809). \n Catch-up: **Yes if a valid binding exists**, 아니면 finding 1/2로 떨어집니다.\n\n5. **Low/intentional: activation 중 ownership을 잃으면 이 프로세스의 output은 포워딩되지 않습니다.** \n CAS가 실패하면 owned runtime start가 중단됩니다 [index.mjs:731](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:731), [index.mjs:736](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:736). 다른 active owner가 보이면 조용히 standby로 갑니다 [index.mjs:916](C:/Project/mixdog-bench/src/runtime/channels/index.mjs:916). \n Catch-up: **No for the losing session**, last-wins 설계상 의도된 동작입니다.\n\n6. **Low: 기존 파일에서 stat 이후 watch 설치 전 쓰인 bytes는 100ms flush로 복구됩니다.** \n `startWatch()`는 watcher 설치 직후 `scheduleWatchFlush()`를 호출합니다 [output-forwarder.mjs:665](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:665), [output-forwarder.mjs:666](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:666). flush는 stat 후 `forwardNewText()`를 실행합니다 [output-forwarder.mjs:703](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:703), [output-forwarder.mjs:724](C:/Project/mixdog-bench/src/runtime/channels/lib/output-forwarder.mjs:724). \n Catch-up: **Yes**, 짧은 지연만 있습니다.\n\n판정: bridge activation window는 pending-file 케이스는 대체로 복구하지만, “새 transcript가 이미 존재하거나 최초 discovery가 빈손인 경우”에는 backlog가 EOF로 조용히 유실될 수 있습니다."
37
+ },
38
+ {
39
+ "id": "r4-impl-usage-summary",
40
+ "agent": "worker",
41
+ "ok": true,
42
+ "ms": 121182,
43
+ "sessionId": "019f27a1-7c61-7e70-bea3-392ebbe5e725",
44
+ "card": {
45
+ "session": "019f27a1-7c61-7e70-bea3-392ebbe5e725",
46
+ "wall_ms": 121182,
47
+ "uncached_in": 51502,
48
+ "cached_in": 392320,
49
+ "out_tokens": 6537,
50
+ "cache_ratio": 0.884,
51
+ "completed": 1
52
+ },
53
+ "output": "현재 워크스페이스가 읽기 전용이라 최종 수정은 적용하지 못했습니다. `apply_patch`가 “writing is blocked by read-only sandbox”로 거절됐습니다.\n\n확인한 현재 diff 요약:\n\n- `package.json`: `usage:today` npm script 1줄 추가됨\n- `scripts/usage-today.mjs`: 신규 파일, 202 lines\n- 별도 기존 변경: `src/runtime/channels/lib/event-queue.mjs`, `src/runtime/channels/lib/output-forwarder.mjs`\n\n검증:\n\n- `node --check scripts/usage-today.mjs`: 통과\n- `npm run usage:today`: 실행 성공, 표 출력됨 \n 단, npm.ps1 접근 경고가 한 줄 있었지만 종료 코드는 0이었습니다.\n\n중요한 점: 현재 `usage-today.mjs`는 `kind:\"usage\"`와 `kind:\"usage_raw\"`를 둘 다 집계해서 비용/토큰이 중복될 수 있습니다. 적용해야 할 최소 수정은 아래 1줄입니다.\n\n```diff\n- if (kind === 'usage' || kind === 'usage_raw') return true;\n+ if (kind === 'usage') return true;\n```\n\n쓰기 권한이 열리면 이 한 줄만 반영한 뒤 다시 `node --check`와 `npm run usage:today`를 돌리면 됩니다."
54
+ },
55
+ {
56
+ "id": "r4-explain-compact-engine",
57
+ "agent": "explore",
58
+ "ok": true,
59
+ "ms": 139884,
60
+ "sessionId": "019f27a1-7df6-7690-aa6d-6734860ac723",
61
+ "card": {
62
+ "session": "019f27a1-7df6-7690-aa6d-6734860ac723",
63
+ "wall_ms": 139884,
64
+ "uncached_in": 97389,
65
+ "cached_in": 1032704,
66
+ "out_tokens": 7491,
67
+ "cache_ratio": 0.914,
68
+ "completed": 1
69
+ },
70
+ "output": "**Entry Points**\n- `session/compact.mjs:52-55` re-exports `semanticCompactMessages` and `recallFastTrackCompactMessages` from `compact/engine.mjs`; the engine exports semantic at `compact/engine.mjs:278` and recall-fasttrack at `compact/engine.mjs:445-446`.\n- The live agent loop imports semantic compaction and recall runner at `session/loop.mjs:10-16` and `session/loop.mjs:32`, then calls recall at `session/loop.mjs:414-428` or semantic at `session/loop.mjs:439-465`.\n- Manual/auto-clear compaction goes through `runSessionCompaction`, imported by `manager.mjs:32-35`, invoked before clear at `manager.mjs:1841-1850`, and exposed as `compactSessionMessages` at `manager.mjs:1943-1957`.\n\n**When It Fires**\n- The loop drains queued steering before deciding, so compaction sees the exact next provider payload: `session/loop.mjs:324-329`.\n- Policy resolution requires auto enabled, derives boundary/trigger/buffer/reserve/type, and marks semantic vs recall-fasttrack: `loop/compact-policy.mjs:89-148`.\n- Pressure is `messageTokensEst + reserveTokens`; compaction fires on forced reactive, unknown estimate, or pressure >= trigger: `loop/compact-policy.mjs:150-174`.\n- If below threshold, the loop only records telemetry: `session/loop.mjs:365-371`; if above, it computes target budget and enters compacting stage: `session/loop.mjs:362-397`.\n- Manager-side compaction skips closed/tiny/non-forced sessions, skips auto below threshold, and otherwise computes target budget: `manager/compaction-runner.mjs:143-198`.\n\n**Preserved Tail**\n- Semantic compaction first sanitizes/dedups, splits protected prefix, finds prior summary, and excludes summary messages from live history: `compact/engine.mjs:100-111`.\n- Summary messages are synthetic `role:'user'` anchors, not real turns: `compact/messages.mjs:19-28`; protected system/reminder prefix is split out at `compact/messages.mjs:56-81`.\n- Semantic tail selection uses user-turn slicing when users exist, otherwise cut-points that never start on `tool`: `compact/engine.mjs:121-128`, `compact/engine.mjs:235-242`.\n- User-turn mode keeps newest turns within recent budget, splitting the newest turn suffix if needed: `compact/engine.mjs:134-185`.\n- No-user mode walks valid cuts newest-to-oldest, tolerates oversized/minimal tails, and avoids empty-head failure cases: `compact/engine.mjs:193-233`.\n- Final semantic window is `head = live[..tailStart]`, repaired `tail = live[tailStart..]`, with preserved facts extracted from head: `compact/engine.mjs:244-258`.\n- Recall-fasttrack keeps newest turns under cap, drops oldest kept turns first, or fits one turn; no-user tails are valid: `compact/engine.mjs:593-619`.\n\n**Redaction**\n- If semantic input already fits and is not forced, it returns sanitized messages unchanged, with no preserved-tail redaction: `compact/engine.mjs:286-312`.\n- When semantic proceeds, tool-call secrets are redacted before window selection, so preserved system/tail are measured and emitted redacted: `compact/engine.mjs:306-314`, `compact/engine.mjs:389-392`.\n- Recall-fasttrack has the same no-op-then-redact-before-split pattern: `compact/engine.mjs:627-646`.\n- Redaction is shape-preserving for verbatim tool-call args and maps only tool-bearing messages: `compact/text-utils.mjs:261-266`, `compact/text-utils.mjs:319-325`.\n\n**Failures / Fallbacks**\n- Semantic throws for missing provider, empty compactable head, unfit prompt, empty summary, unfit summary, or final budget overflow: `compact/engine.mjs:278-280`, `compact/engine.mjs:315-317`, `compact/engine.mjs:335-337`, `compact/engine.mjs:371-397`.\n- Mandatory preserved context can raise the working budget instead of refusing: `compact/engine.mjs:319-331`; recall-fasttrack mirrors this at `compact/engine.mjs:658-664`.\n- Loop failures try one non-LLM unanchored prune only for non-recall failures: `session/loop.mjs:500-537`; recall failures are explicitly excluded from that fallback: `session/loop.mjs:511-517`.\n- If fallback prune fails, loop records telemetry/trace/event and throws `AgentContextOverflowError`: `session/loop.mjs:546-626`.\n- Provider context overflow triggers one reactive compact retry via the normal pre-send path; after retry it surfaces overflow: `session/loop.mjs:982-1026`.\n- Manager runner degrades recall-fasttrack failure to semantic if possible, then tries non-recall prune, then records failure or replaces `session.messages` and clears provider state: `manager/compaction-runner.mjs:237-278`, `manager/compaction-runner.mjs:322-347`, `manager/compaction-runner.mjs:348-411`."
71
+ }
72
+ ],
73
+ "task_complete": true,
74
+ "task_errors": [],
75
+ "score_complete": true,
76
+ "score_errors": [],
77
+ "group": {
78
+ "n": 3,
79
+ "wall_ms": 167128.7,
80
+ "uncached_in": 87848.3,
81
+ "cached_in": 802048,
82
+ "out_tokens": 9567.7,
83
+ "cache_ratio": 0.9,
84
+ "completed": 1
85
+ },
86
+ "cards": [
87
+ {
88
+ "session": "019f27a1-7dc4-7eb0-8195-f06a7c0022a3",
89
+ "wall_ms": 240320,
90
+ "uncached_in": 114654,
91
+ "cached_in": 981120,
92
+ "out_tokens": 14675,
93
+ "cache_ratio": 0.895,
94
+ "completed": 1
95
+ },
96
+ {
97
+ "session": "019f27a1-7c61-7e70-bea3-392ebbe5e725",
98
+ "wall_ms": 121182,
99
+ "uncached_in": 51502,
100
+ "cached_in": 392320,
101
+ "out_tokens": 6537,
102
+ "cache_ratio": 0.884,
103
+ "completed": 1
104
+ },
105
+ {
106
+ "session": "019f27a1-7df6-7690-aa6d-6734860ac723",
107
+ "wall_ms": 139884,
108
+ "uncached_in": 97389,
109
+ "cached_in": 1032704,
110
+ "out_tokens": 7491,
111
+ "cache_ratio": 0.914,
112
+ "completed": 1
113
+ }
114
+ ]
115
+ }
@@ -0,0 +1,120 @@
1
+ {
2
+ "round": "34",
3
+ "runner": "lead",
4
+ "opts": {
5
+ "provider": "openai-oauth",
6
+ "model": "gpt-5.5",
7
+ "effort": null,
8
+ "fast": false,
9
+ "workflow": "default",
10
+ "env": {
11
+ "MIXDOG_DATA_DIR": "C:\\Users\\tempe\\.mixdog\\bench-data"
12
+ }
13
+ },
14
+ "tasks": 1,
15
+ "completed": 1,
16
+ "completion_rate": 100,
17
+ "sessions": [
18
+ "sess_23752_1783076753474_1783076753723_263c02f99dbb98fd9e99bc46c41bb5b8"
19
+ ],
20
+ "results": [
21
+ {
22
+ "id": "r5-orchestrated-audit",
23
+ "agent": "lead",
24
+ "ok": true,
25
+ "ms": 320527,
26
+ "sessionId": "sess_23752_1783076753474_1783076753723_263c02f99dbb98fd9e99bc46c41bb5b8",
27
+ "output": "sessionId: sess_23752_1783076753474_1783076753723_263c02f99dbb98fd9e99bc46c41bb5b8\nScope B 교차검토는 완료됐고, 주요 결론은 유지됩니다.\n\n- **확정:** handshake 5회, midstream 4/2회 예산, retry마다 fresh acquire, HTTP 5xx 누적 재시도 가능성이 확인됐습니다.\n- **추가 발견:** `openai-oauth-ws.mjs:507-508` 주석이 현재 코드의 4/2회 midstream 예산과 맞지 않는 stale comment입니다.\n- **대기 중:** Scope A reviewer 완료 후 두 범위를 합쳐 최종 보고서를 내겠습니다.분석 완료 — 두 scope와 리뷰 결과를 합친 최종 보고서입니다. 파일 수정은 하지 않았습니다.\n\n- **Classifier bucket inventory:** `classifyError()`는 `auth|permanent|transient|unknown`이고, WS midstream은 `first_byte_timeout`, `ws_send_failed`, `http_${status}`, `agent_stall`, `stream_stalled`, `ws_1006`, `ws_1011`, `ws_1012`, `ws_4000`, `ws_1000`, `response_failed_disconnected`, `response_failed_network`, `response_failed_auth_expired`, `null`을 반환합니다 (`retry-classifier.mjs:100`, `:293-338`).\n\n- **Reachability:** 대부분은 local producer가 있습니다. 예: `ws_send_failed`는 `_sendFrame` 실패 태그(`openai-ws-pool.mjs:106-134`), first-byte timeout은 `err.firstByteTimeout`(`openai-ws-stream.mjs:459-485`), response.failed payload는 `midState.responseFailedPayload`(`openai-ws-stream.mjs:953-963`)를 통해 소비됩니다.\n\n- **No local producer:** `response_failed_auth_expired`는 `response.failed` payload 문자열 `\"auth context expired\"`에만 의존하며(`retry-classifier.mjs:332-338`), repo 내 해당 문자열 producer는 classifier 자신뿐입니다. 즉 로컬 코드 경로는 만들 수 없고 upstream 서버 문자열에만 의존합니다.\n\n- **Stale docs/comments:** `_classifyMidstreamError` 주석은 실제 bucket 중 `http_${status}`, `ws_send_failed`, `response_failed_auth_expired`를 누락합니다(`openai-oauth-ws.mjs:124-139`). `ws_4000` 설명도 first-byte path가 실제로는 `first_byte_timeout`에서 먼저 잡히므로 오해 소지가 있습니다(`retry-classifier.mjs:293-329`, `openai-ws-stream.mjs:467-480`).\n\n- **Handshake loop:** `_acquireWithRetry`는 최대 5회 시도(`stall-policy.mjs:200-201`, `openai-oauth-ws.mjs:104`, `:389`)하고, `500ms * 2^(attempt-1)`를 5000ms cap으로 jitter 처리합니다(`openai-oauth-ws.mjs:175-178`, `:427-459`). 401/403/404/429는 retry하지 않고, 5xx와 transient socket 계열만 classifier로 재시도됩니다(`retry-classifier.mjs:477-489`).\n\n- **Midstream loop:** `sendViaWebSocket`는 `attemptIndex <= MAX_MIDSTREAM_RETRIES`로 최대 5번 stream attempt를 돌며(`openai-oauth-ws.mjs:514`, `:557`), 각 retry는 fresh acquire를 다시 수행합니다(`:564-572`). `ws_1006/ws_1011`은 4회 retry, 그 외 bucket은 2회 retry이고 backoff는 `[250,1000,2000,4000]` jitter입니다(`retry-classifier.mjs:245-268`, `openai-oauth-ws.mjs:164-172`, `:843-860`).\n\n- **Double-retry risk:** HTTP 5xx는 handshake에서 `http_${status}`로 재시도되고(`retry-classifier.mjs:480-482`), midstream에서도 같은 5xx 상태가 retry bucket이 됩니다(`:306-310`). 따라서 persistent/flapping 5xx는 midstream 재시도마다 fresh acquire 5회 예산을 다시 얻어 `sendViaWebSocket` 내부에서 최대 25 acquire 시도까지 누적될 수 있습니다(`openai-oauth-ws.mjs:557`, `:564-572`).\n\n- **Caller layer:** WS 실패 뒤 401/403은 live text가 없을 때 auth refresh 후 WS 1회 재실행이고(`openai-oauth.mjs:951-976`), fallback 대상이면 HTTP/SSE로 1회 전환합니다(`openai-oauth.mjs:989-995`, `retry-classifier.mjs:398-413`). 401/403/429는 handshake/midstream 내부 retry와 겹치지 않습니다(`retry-classifier.mjs:477-479`, `:307`).",
28
+ "card": {
29
+ "session": "sess_23752_1783076753474_1783076753723_263c02f99dbb98fd9e99bc46c41bb5b8",
30
+ "provider": "openai-oauth",
31
+ "model": "gpt-5.5",
32
+ "wall_ms": 309924,
33
+ "turns": 56,
34
+ "tool_calls": 65,
35
+ "tools_per_turn": 1.2,
36
+ "total_tool_ms": 5015,
37
+ "llm_stream_ms": 522741,
38
+ "cache_ratio": 0.8561893645226001,
39
+ "cached_tokens": 1459683,
40
+ "prompt_tokens": 1704860,
41
+ "uncached_tokens": 51582,
42
+ "output_tokens": 24458,
43
+ "cache_weighted_input_10": 197550.30000000002,
44
+ "cache_weighted_input_25": 416502.75,
45
+ "prompt_growth": 45489,
46
+ "antipatterns": 3,
47
+ "issues": 23,
48
+ "issue_types": [
49
+ "readonly_stall",
50
+ "cache",
51
+ "cache_break",
52
+ "broad_tool_result",
53
+ "service_tier",
54
+ "missed_parallelism",
55
+ "tool_churn_cluster"
56
+ ],
57
+ "read_fragmentation_paths": [],
58
+ "grep_sweep_count": 0
59
+ }
60
+ }
61
+ ],
62
+ "task_complete": true,
63
+ "task_errors": [],
64
+ "score_complete": true,
65
+ "score_errors": [],
66
+ "group": {
67
+ "n": 1,
68
+ "wall_ms": 309924,
69
+ "turns": 56,
70
+ "tool_calls": 65,
71
+ "tools_per_turn": 1.2,
72
+ "total_tool_ms": 5015,
73
+ "llm_stream_ms": 522741,
74
+ "cache_ratio": 0.9,
75
+ "cached_tokens": 1459683,
76
+ "prompt_tokens": 1704860,
77
+ "uncached_tokens": 51582,
78
+ "output_tokens": 24458,
79
+ "cache_weighted_input_10": 197550.3,
80
+ "cache_weighted_input_25": 416502.8,
81
+ "prompt_growth": 45489,
82
+ "antipatterns": 3,
83
+ "issues": 23,
84
+ "grep_sweep_count": 0
85
+ },
86
+ "cards": [
87
+ {
88
+ "session": "sess_23752_1783076753474_1783076753723_263c02f99dbb98fd9e99bc46c41bb5b8",
89
+ "provider": "openai-oauth",
90
+ "model": "gpt-5.5",
91
+ "wall_ms": 309924,
92
+ "turns": 56,
93
+ "tool_calls": 65,
94
+ "tools_per_turn": 1.2,
95
+ "total_tool_ms": 5015,
96
+ "llm_stream_ms": 522741,
97
+ "cache_ratio": 0.8561893645226001,
98
+ "cached_tokens": 1459683,
99
+ "prompt_tokens": 1704860,
100
+ "uncached_tokens": 51582,
101
+ "output_tokens": 24458,
102
+ "cache_weighted_input_10": 197550.30000000002,
103
+ "cache_weighted_input_25": 416502.75,
104
+ "prompt_growth": 45489,
105
+ "antipatterns": 3,
106
+ "issues": 23,
107
+ "issue_types": [
108
+ "readonly_stall",
109
+ "cache",
110
+ "cache_break",
111
+ "broad_tool_result",
112
+ "service_tier",
113
+ "missed_parallelism",
114
+ "tool_churn_cluster"
115
+ ],
116
+ "read_fragmentation_paths": [],
117
+ "grep_sweep_count": 0
118
+ }
119
+ ]
120
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "round": "35",
3
+ "runner": "codex",
4
+ "opts": {
5
+ "provider": "openai-oauth",
6
+ "model": "gpt-5.5",
7
+ "effort": null,
8
+ "fast": false,
9
+ "workflow": null,
10
+ "env": {}
11
+ },
12
+ "tasks": 1,
13
+ "completed": 1,
14
+ "completion_rate": 100,
15
+ "sessions": [
16
+ "019f27b1-8fef-71e0-ba3d-9c104a0eb324"
17
+ ],
18
+ "results": [
19
+ {
20
+ "id": "r5-orchestrated-audit",
21
+ "agent": "lead",
22
+ "ok": true,
23
+ "ms": 327028,
24
+ "sessionId": "019f27b1-8fef-71e0-ba3d-9c104a0eb324",
25
+ "card": {
26
+ "session": "019f27b1-8fef-71e0-ba3d-9c104a0eb324",
27
+ "wall_ms": 327028,
28
+ "uncached_in": 74996,
29
+ "cached_in": 997888,
30
+ "out_tokens": 14950,
31
+ "cache_ratio": 0.93,
32
+ "completed": 1
33
+ },
34
+ "output": "분석 전용으로 진행했고 파일은 수정하지 않았습니다. A/B를 별도 워커에 병렬 위임한 뒤, 제가 원문 라인과 대조해 병합했습니다.\n\n**Scope A**\n`openai-oauth-ws.mjs`가 직접 소비하는 shared classifier는 `classifyHandshakeError`와 `classifyMidstreamError`뿐입니다. import는 [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:33), wrapper는 [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:112)와 [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:157)에 있습니다.\n\n`classifyError()` bucket:\n`auth`, `permanent`, `transient`, `unknown`입니다. 각각 401/403, unsafe/429/permanent 4xx, truncation/5xx/socket errno, fallback unknown으로 생산됩니다: [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:100), [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:105), [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:113), [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:117). 단, `openai-oauth-ws.mjs`는 이 classifier를 직접 호출하지 않습니다.\n\nHandshake bucket:\n`timeout`, `reset`, `dns`, `refused`, `network`, `acquire_timeout`, numeric `http_<status>` 예: `http_503`, 그리고 `null`입니다. 생산 근거는 [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:477)부터 [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:494)입니다. 소비는 `_acquireWithRetry()`의 [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:403)에서 하며, `null`은 즉시 throw, non-null은 retry/tag 처리됩니다.\n\nWS midstream bucket:\n`first_byte_timeout`, `ws_send_failed`, numeric `http_<status>`, `agent_stall`, `stream_stalled`, `ws_1006`, `ws_1011`, `ws_1012`, `ws_4000`, `ws_1000`, `response_failed_disconnected`, `response_failed_network`, `response_failed_auth_expired`, `null`입니다. 분류기는 [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:286)부터 [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:342)이고, 소비는 [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:843)부터 [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:860)입니다. 실제 생산 근거는 first-byte timeout [openai-ws-stream.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs:466), send failure [openai-ws-pool.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs:106), response.failed [openai-ws-stream.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs:953), close code [openai-ws-stream.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs:1001)입니다.\n\nOpenAI WS에서 도달 불가:\nSSE path bucket인 `sse_idle_timeout`, `stream_terminated`, `fetch_failed`는 `policy.mode === 'sse'`에서만 나옵니다: [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:348). OpenAI WS는 항상 `mode: 'ws'`를 전달합니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:87).\n\nNo-producer bucket:\nliteral `http_5xx`는 주석/allow-list에는 있지만 실제 producer가 없습니다. allow-list는 [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:386), 주석은 [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:469), 실제 producer는 numeric `http_${status}`입니다: [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:309), [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:481). Regex fallback이 numeric form은 받습니다: [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:410).\n\n**Scope B**\nHandshake retry budget은 5 attempts입니다. `HANDSHAKE_MAX_ATTEMPTS = PROVIDER_RETRY_MAX_ATTEMPTS`이고, 값은 backoff array length 5입니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:104), [stall-policy.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/stall-policy.mjs:200). Loop는 `attempt = 1..5`: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:389). Backoff raw는 500/1000/2000/4000ms, cap 5000ms, symmetric jitter 기본 0.2입니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:175), [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:222), [stall-policy.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/stall-policy.mjs:202).\n\nMidstream retry budget은 `ws_1006`/`ws_1011`만 4 retries, 나머지 retryable WS bucket은 2 retries입니다: [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:245), [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:251), [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:164). Loop 자체는 max 4 retries까지 돌 수 있지만 per-classifier gate가 적용됩니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:557), [retry-classifier.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/retry-classifier.mjs:266). Backoff raw는 250/1000/2000/4000ms + jitter입니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:170), [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:852).\n\nDouble-retry 결론:\n같은 error object가 handshake와 midstream 양쪽에서 동시에 retry되지는 않습니다. acquire 실패는 별도 catch에서 처리되고 midstream classifier로 가지 않습니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:563). midstream classification은 acquire 성공 이후 send/stream catch에서만 실행됩니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:668), [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:843). 다만 midstream retry가 발생하면 다음 attempt가 fresh acquire를 하므로 handshake budget이 순차적으로 다시 붙을 수 있습니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:571).\n\n추가 개선 포인트:\n`openai-oauth-ws.mjs` 주석은 아직 `ws_1006/ws_1011 get two retries`와 `up to 2`라고 되어 있지만 실제 정책은 4 retries입니다: [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:507), [openai-oauth-ws.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs:1113). 또 local inter-chunk timeout은 4000 close를 호출하지만 error에 `wsCloseCode`를 붙이지 않고 바로 `finish()`하므로 `ws_4000`이 아니라 `null`로 끝날 가능성이 큽니다: [openai-ws-stream.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs:526), [openai-ws-stream.mjs](C:/Project/mixdog/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs:530).\n\n변경 파일: 없음. 테스트 실행: 없음, 정적 분석과 라인 근거 교차검증만 수행했습니다."
35
+ }
36
+ ],
37
+ "task_complete": true,
38
+ "task_errors": [],
39
+ "score_complete": true,
40
+ "score_errors": [],
41
+ "group": {
42
+ "n": 1,
43
+ "wall_ms": 327028,
44
+ "uncached_in": 74996,
45
+ "cached_in": 997888,
46
+ "out_tokens": 14950,
47
+ "cache_ratio": 0.9,
48
+ "completed": 1
49
+ },
50
+ "cards": [
51
+ {
52
+ "session": "019f27b1-8fef-71e0-ba3d-9c104a0eb324",
53
+ "wall_ms": 327028,
54
+ "uncached_in": 74996,
55
+ "cached_in": 997888,
56
+ "out_tokens": 14950,
57
+ "cache_ratio": 0.93,
58
+ "completed": 1
59
+ }
60
+ ]
61
+ }