chainlesschain 0.166.56 → 0.166.58

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 (173) hide show
  1. package/package.json +2 -1
  2. package/src/assets/web-panel/assets/{AIOps-CBFnwYRX.js → AIOps-DoyI29Aw.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Sl4eXWpR.js → ActionButton-eZKcEoxc.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DB0lD9Kb.js → Analytics-B2C2e9A7.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-j77EmkRK.js → AppLayout-DkZJiu4X.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CU3izCW9.js → Artifacts-8PD3BXVa.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-C44fSibb.js → Audit-Cv6sRlFu.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-pM_NnJW3.js → BackgroundAgents-D-V84Qpm.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-AOHXqhxN.js → Backup-DYJQcxj1.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DaYc8oA_.js → BaseInput-KL2IeywH.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CmlCAXxW.js → Chat-Bt9wy_Yn.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CkCSkJDJ.js → ChatBubbleRenderer-ChB6fH-W.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-DISI7iaA.js → Checkbox-Dm-nfdnd.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BYpeKUpf.js → Codegen-CYi3yDzv.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Dk6VwWGq.js → Col-C7sQywfG.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-BDyTedMu.js → Community-DjxuqAM4.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CTNlGM9X.js → Compact-CD_4lbxr.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-C_tw3IdT.js → Compliance-BtDpNJrS.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-cduHWEmo.js → Cowork-l19ANH6Q.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-Dqotjv-s.js → Cron-Br-rBoEK.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-ByW7RsJQ.js → Crosschain-60UVvKET.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BI-lafPj.js → DID-B9dqar51.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-8m50TRZL.js → Dashboard-B39hhdHb.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CN3hmRTc.js → Dropdown-Bm5rCfvF.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-t0lzxsDT.js → EmailListRenderer-BI9HANDl.js} +1 -1
  26. package/src/assets/web-panel/assets/{EvolutionSettings-DabpA3cQ.js → EvolutionSettings-Cfqn_yCq.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BYN4UshY.js → FamilyGuardDashboard-9Y4_zyhT.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-n44OtIXp.js → Federation-vnbbmMdF.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-B-WXDu_I.js → FormItemContext-BEKNtNF9.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-Bm7v-mTf.js → GenericCardRenderer-BmWr16IL.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-BMIS6sbo.js → Git-CUUwAXdn.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-B1EhHobE.js → Governance-Bw8XZmH9.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-Dy_HJWHo.js → Inference-CNrml6dn.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-BVdfUbZy.js → KnowledgeGraph-BhxOwpW_.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-Dph_oLQk.js → Logs-BV8WViUG.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-CO1qbiOY.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-Bw4HWORj.js → Marketplace-ztcu4mg4.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-Be6DPfA1.js → McpTools-D-wzRO7z.js} +2 -2
  39. package/src/assets/web-panel/assets/{Memory-DlvLafC9.js → Memory-ChdjWC1R.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-Upc_Jwlt.js → MobileBridge-B1QHzIjE.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-I17jrUbY.js → MobileProjects-DmaRtaMw.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-DyBzY0GH.js → Mtc-DRQMr4WP.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-2-7xAwEo.js → MtcAudit-Cwo0on-u.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-DtUZpFmZ.js → Multisig-QplgySFt.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-iInH7jBr.js → NLProgramming-BWkMyuAy.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-BO3Z3vaS.js → Notes-BI_pSEM0.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-ClX_0jFE.js → NotificationSettings-DiuFo6Xe.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-Czw742w4.js → OrderTableRenderer-DZzQhujZ.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BATEx24i.js → Organization-DqEdqWB0.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-B_PXMNia.js → Overflow-CO7_2YLN.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BSiE8PqT.js → P2P-BN5RiNst.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-BbklUgZY.js → PdhVaultBrowser-CapfG4bl.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-B7bpflT_.js → Permissions-B24c5fWW.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-BWZS4BLy.js → PersonalDataHub-D7Mb3_vh.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-DP2YSs6H.js → Pipeline-BsfxlbOQ.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-DB9ptoH0.js → Privacy-Bq7c7IYy.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-BAfePyYk.js → ProjectInit-Clse2DPl.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-BEXn4Cm3.js → ProjectSettings-CHAEdjri.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-C1UHvBEK.js +1 -0
  60. package/src/assets/web-panel/assets/{Providers-CRX9TdRO.js → Providers-DQyH3PXU.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-DUWcSY5L.js → QrScannerModal-t2Z-N49o.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-Cgo3Z_hs.js → QuickAsk-BacR7PlI.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-Dcinx0IW.js → Recommend-D-NAy0CQ.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-BMZzTnAN.js → RemoteSession-CjI3GeVQ.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-BdZzpOp9.js → Reputation-HoQ3i2oW.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DqM8GVcx.js → Row-CY2jczWa.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Soj_QBK3.js → RssFeed-Bd3o20IY.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-Cn2es0-v.js → Search-C88KjgTv.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-t7C4hz3n.js → Security-yfmfvujK.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-ChF-e_Uf.js → Services-DLPBWoxT.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-BiPIyK-2.js → Skeleton-DzDjvVQi.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-HgkKJenJ.js → Skills-DqfrJIyP.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-BSfluv_k.js → Sla-BXhsGzkm.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-Sm0Gz9tM.js → SpeechSettings-jxSl2JDo.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-DlhBMnfU.js → SyncSettings-BX2xKtI3.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-C7OUvsnp.js → Tasks-P9MmRYt4.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-CrMtw87y.js → Templates-CTpj9f62.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DSNSB6j5.js → Tenant-nnQuhdPg.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-Cmh78FQg.js → Terminal-cJ0vbuTe.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-BE8b8lHR.js → TimelineRenderer-DN08D0SD.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BrtgdIvJ.js → Tokens-C8BWWBUD.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-HCmaoKDp.js → Trigger-DhD-LUoe.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-GASQ4F-2.js → Trust-D0nFyc_Z.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-B8kSzWGr.js → UkeySign-B59G57LP.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-Nn-clhv5.js → VideoEditing-BpYACxVC.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-DdYMKTYk.js → Wallet-DlZ3rKGD.js} +3 -3
  87. package/src/assets/web-panel/assets/{WebAuthn-xJXUr_eK.js → WebAuthn-De4dNyig.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CbSa87av.js → WorkflowEditor-C2Tewnhp.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-CVrNLGUV.js → chat-CDBn4JLE.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-e2veLfIA.js → colors-BNawl0d_.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-CNd7nhUq.js → compact-item-n65WDhze.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-DEudTVqv.js → createContext-DAKgOdIy.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-sBSSW127.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-BlSAFZa3.js → hasIn-DhxFG46W.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CU4Y4VVN.js → index--76HBS1k.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BmVFeSEq.js → index-0Y-ncsVy.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DtrqgNBl.js → index-5oJjovnA.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BRNCKg4q.js → index-BAogaHa9.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-0y7tHZdT.js → index-BBFjMjmp.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-D_D7lcnQ.js → index-BDTrf8WH.js} +1 -1
  101. package/src/assets/web-panel/assets/index-BIJTDSLY.js +1 -0
  102. package/src/assets/web-panel/assets/{index-DyFZ7gDd.js → index-BQFAuU1c.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CK6Rb66R.js → index-BRyzRluv.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-D7yJceTZ.js → index-BZI8xwwt.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CHMrAZiR.js → index-BfXdTvku.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Bp4Gcn9u.js → index-BudfMkBy.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B_UNX_xq.js → index-By-zGgUp.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Jk99aZIr.js → index-ByDs4cW4.js} +3 -3
  109. package/src/assets/web-panel/assets/{index-BpH6T1gA.js → index-BzONk4rW.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Bqeb4jnC.js → index-CE6OcLof.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CYNoXhxl.js → index-CSOgoyqx.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BptI9bLu.js → index-CW_OUdP8.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-MuTb62K6.js → index-CZJrAg98.js} +1 -1
  114. package/src/assets/web-panel/assets/index-CrBauOkp.js +1 -0
  115. package/src/assets/web-panel/assets/{index-ClROBPWd.js → index-Cv7LqHwv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BG-VNepa.js → index-D34AXxN9.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DFv-nsIT.js → index-D6sWUgN6.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Bwtt-0CQ.js → index-DCujBaFg.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B1C1wS5c.js → index-DFkamspD.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C6zGNZ5H.js → index-DLFRz0_7.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C4h_Bvd2.js → index-DV6iHrUO.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-7-7rr4Mf.js → index-D_-6qQf0.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CgVwz2jD.js → index-DeTlqus4.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DwjWRUTf.js → index-DkW0HGr-.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-4ZT0vIJt.js → index-DleKcclQ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-hzUfIn4u.js → index-Ds290C06.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DFzvcZ3Q.js → index-KT94nMha.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CWuv47z0.js → index-KZkt4e9N.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Lm9pm1zm.js → index-L1QnA0gW.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Ec6VBiV-.js → index-PlOBwfR1.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DVORJydf.js → index-gG1WJBpa.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BMIhGZUR.js → index-hA9VUXGr.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DJ5gsewX.js → index-pzEYIGNA.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-C6fpNcA5.js → initDefaultProps-B3bCivrt.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-Bjh9Come.js → motion-CF5pGsxz.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DFa4ul-_.js → move-CaOFtFLU.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-KoYqA_e5.js → mtc-parser-Z4-50Y1c.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-ByTjP-wG.js → omit-AK4WqVTT.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-CG_rmTxx.js → pickAttrs-Dbye0px4.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-LDePZ8V9.js → placementArrow-DCmr-Dks.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-DZFzH4RR.js → responsiveObserve-CJ3Zm_-H.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-DfXQyTvD.js → slide-BuSk5-ue.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-CuhqsJfO.js → statusUtils-8u_lKSFJ.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-qqVjJw5u.js → styleChecker-sy6_m42F.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-BPxnCxcb.js → useFlexGapSupport-Di5EfYGo.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-D11NLCjV.js → useFs-BTfX_Ri1.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-HbZVcaCX.js → usePersonalDataHub-kqurO26X.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-CZHQlfTu.js → vnode-DRkH4mPV.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CV3rUknJ.js → zoom-ON_-UuD_.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agent.js +5 -0
  152. package/src/commands/config.js +19 -10
  153. package/src/commands/eval.js +39 -4
  154. package/src/data/changelog.json +26 -0
  155. package/src/lib/diagnostic-excerpt.js +51 -0
  156. package/src/lib/diagnostic-workflow.js +10 -0
  157. package/src/lib/eval/evidence.js +19 -10
  158. package/src/lib/eval/outcomes.js +373 -0
  159. package/src/lib/eval/runner.js +6 -0
  160. package/src/lib/git-tool-input.js +37 -0
  161. package/src/lib/llm-config-defaults.js +32 -9
  162. package/src/lib/read-file-loop-guard.js +54 -0
  163. package/src/lib/remote-read-loop-guard.js +12 -14
  164. package/src/lib/session-message-fabric.js +74 -37
  165. package/src/lib/shell-output.js +30 -0
  166. package/src/lib/task-progress-tracker.js +88 -0
  167. package/src/runtime/agent-core.js +95 -22
  168. package/src/runtime/headless-stream.js +25 -0
  169. package/src/assets/web-panel/assets/MarkdownRenderer-CNHYqcxm.js +0 -1
  170. package/src/assets/web-panel/assets/Projects-BzSs0rzn.js +0 -1
  171. package/src/assets/web-panel/assets/devWarning-DW7he-SA.js +0 -1
  172. package/src/assets/web-panel/assets/index-IkoHuWRe.js +0 -1
  173. package/src/assets/web-panel/assets/index-MwqsDbb1.js +0 -1
@@ -0,0 +1,373 @@
1
+ import {
2
+ evalDigest,
3
+ evalHistoryRecordIssues,
4
+ evalExecutionSucceeded,
5
+ } from "./evidence.js";
6
+
7
+ export const OUTCOME_PLAN_SCHEMA = "chainlesschain.outcome-plan/v1";
8
+ export const OUTCOME_REPORT_SCHEMA = "chainlesschain.outcome-report/v1";
9
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
10
+ const CATEGORIES = new Set([
11
+ "repair",
12
+ "support",
13
+ "compatibility",
14
+ "release",
15
+ "test-infrastructure",
16
+ "construction",
17
+ ]);
18
+ const STAGES = ["install", "configure", "authenticate", "tool", "artifact"];
19
+ const FAILURE_CAUSES = new Set([
20
+ "install",
21
+ "configuration-identity",
22
+ "model-protocol",
23
+ "tools-permissions",
24
+ "retrieval-context",
25
+ "recovery",
26
+ "artifact",
27
+ "environment",
28
+ "budget",
29
+ "manual-repair",
30
+ ]);
31
+ const nonempty = (value) =>
32
+ typeof value === "string" && value.trim().length > 0;
33
+ const nonnegative = (value) => Number.isFinite(value) && value >= 0;
34
+ const count = (value) => Number.isSafeInteger(value) && value >= 0;
35
+ const rate = (numerator, denominator) =>
36
+ denominator ? numerator / denominator : null;
37
+ function assert(value, message) {
38
+ if (!value) throw new Error(message);
39
+ }
40
+ function canonical(value) {
41
+ if (Array.isArray(value)) return value.map(canonical);
42
+ if (!value || typeof value !== "object") return value;
43
+ return Object.fromEntries(
44
+ Object.keys(value)
45
+ .sort()
46
+ .map((key) => [key, canonical(value[key])]),
47
+ );
48
+ }
49
+ export function outcomeDigest(value) {
50
+ return evalDigest(JSON.stringify(canonical(value)));
51
+ }
52
+ function epoch(value) {
53
+ const time = Date.parse(value);
54
+ return typeof value === "string" &&
55
+ Number.isFinite(time) &&
56
+ new Date(time).toISOString() === value
57
+ ? time
58
+ : NaN;
59
+ }
60
+ function uniqueIndex(rows, key, label) {
61
+ assert(Array.isArray(rows), `${label} must be an array`);
62
+ const index = new Map();
63
+ for (const row of rows) {
64
+ assert(
65
+ row && nonempty(row[key]) && !index.has(row[key]),
66
+ `${label} contains a missing or duplicate ${key}`,
67
+ );
68
+ index.set(row[key], row);
69
+ }
70
+ return index;
71
+ }
72
+
73
+ export function validateOutcomePlan(plan) {
74
+ assert(plan?.schema === OUTCOME_PLAN_SCHEMA, "invalid outcome plan schema");
75
+ assert(
76
+ /^[a-f0-9]{40}$/u.test(plan.commitSha || ""),
77
+ "plan requires an exact commit SHA",
78
+ );
79
+ assert(/^[A-Z]{3}$/u.test(plan.currency || ""), "plan requires one currency");
80
+ assert(
81
+ epoch(plan.window?.start) < epoch(plan.window?.end),
82
+ "invalid observation window",
83
+ );
84
+ const samples = uniqueIndex(plan.samples, "id", "plan samples");
85
+ assert(
86
+ samples.size > 0 && samples.size <= 10000,
87
+ "plan requires 1–10000 samples",
88
+ );
89
+ for (const sample of samples.values()) {
90
+ assert(["task", "first-run"].includes(sample.kind), "invalid sample kind");
91
+ assert(
92
+ nonempty(sample.taskId) && nonempty(sample.stratum),
93
+ "sample requires taskId and stratum",
94
+ );
95
+ assert(
96
+ DIGEST.test(sample.comparisonDigest || ""),
97
+ "sample requires an Eval comparison digest",
98
+ );
99
+ assert(
100
+ nonnegative(sample.costBudget) &&
101
+ Number.isFinite(sample.timeBudgetMs) &&
102
+ sample.timeBudgetMs > 0,
103
+ "sample requires finite cost and time budgets",
104
+ );
105
+ }
106
+ return samples;
107
+ }
108
+
109
+ function summarize(rows) {
110
+ const succeeded = rows.filter((row) => row.succeeded).length;
111
+ const observed = rows.filter((row) => row.observed).length;
112
+ const knownCost = rows.reduce((sum, row) => sum + (row.cost ?? 0), 0);
113
+ const costsComplete = rows.every((row) => row.observed && row.cost !== null);
114
+ return {
115
+ planned: rows.length,
116
+ observed,
117
+ missing: rows.length - observed,
118
+ succeeded,
119
+ successRate: rate(succeeded, rows.length),
120
+ knownCost,
121
+ totalCost: costsComplete ? knownCost : null,
122
+ costPerSuccess: costsComplete && succeeded ? knownCost / succeeded : null,
123
+ retries: rows.every((row) => row.retries !== null)
124
+ ? rows.reduce((sum, row) => sum + row.retries, 0)
125
+ : null,
126
+ manualRepairs: rows.every((row) => row.manualRepairs !== null)
127
+ ? rows.reduce((sum, row) => sum + row.manualRepairs, 0)
128
+ : null,
129
+ };
130
+ }
131
+
132
+ /** Read-only measurement. Declarations and local Eval files are not attestations. */
133
+ export function buildOutcomeReport(
134
+ { plan, history, observations = [], maintenance = null },
135
+ { now = Date.now(), expectedPlanDigest } = {},
136
+ ) {
137
+ const samples = validateOutcomePlan(plan);
138
+ assert(Number.isFinite(now), "invalid report time");
139
+ const planDigest = outcomeDigest(plan);
140
+ assert(
141
+ !expectedPlanDigest || expectedPlanDigest === planDigest,
142
+ "frozen plan digest mismatch",
143
+ );
144
+ const observed = uniqueIndex(observations, "sampleId", "observations");
145
+ for (const id of observed.keys())
146
+ assert(samples.has(id), "observation is outside the frozen plan");
147
+ const runs = uniqueIndex(history?.runs ?? [], "runId", "Eval history");
148
+ const issues = (history?.issues ?? []).map((issue) => issue.code);
149
+ if (epoch(plan.window.end) > now)
150
+ issues.push("observation_window_not_closed");
151
+ const usedEvidence = new Set();
152
+ const rows = [];
153
+ for (const sample of samples.values()) {
154
+ const observation = observed.get(sample.id);
155
+ const reasons = [];
156
+ const row = {
157
+ sampleId: sample.id,
158
+ kind: sample.kind,
159
+ taskId: sample.taskId,
160
+ stratum: sample.stratum,
161
+ observed: Boolean(observation),
162
+ succeeded: false,
163
+ reasons,
164
+ cost: null,
165
+ elapsedMs: null,
166
+ retries: null,
167
+ manualRepairs: null,
168
+ failureCause: null,
169
+ firstRunCompletedStages: [],
170
+ };
171
+ rows.push(row);
172
+ if (!observation) {
173
+ reasons.push("missing_observation");
174
+ issues.push(`${sample.id}:missing_observation`);
175
+ continue;
176
+ }
177
+ for (const key of ["cost", "elapsedMs", "retries", "manualRepairs"]) {
178
+ const valid = ["retries", "manualRepairs"].includes(key)
179
+ ? count(observation[key])
180
+ : nonnegative(observation[key]);
181
+ if (valid) row[key] = observation[key];
182
+ else reasons.push(`missing_or_invalid_${key}`);
183
+ }
184
+ const observedAt = epoch(observation.observedAt);
185
+ if (!(
186
+ observedAt <= Math.min(now, epoch(plan.window.end)) &&
187
+ observedAt - (row.elapsedMs ?? 0) >= epoch(plan.window.start)
188
+ ))
189
+ reasons.push("observation_outside_window");
190
+ const run = runs.get(observation.runId);
191
+ const stoppedBeforeTask =
192
+ sample.kind === "first-run" &&
193
+ observation.runId === null &&
194
+ observation.firstRun?.stages?.tool?.passed === false;
195
+ const evidenceKey = JSON.stringify([observation.runId, sample.taskId]);
196
+ if (observation.runId !== null) {
197
+ assert(
198
+ !usedEvidence.has(evidenceKey),
199
+ "a task execution cannot count as multiple samples",
200
+ );
201
+ usedEvidence.add(evidenceKey);
202
+ }
203
+ if (!run && !stoppedBeforeTask) reasons.push("missing_eval_run");
204
+ if (run) {
205
+ // Failed executions are valid observations, not successful outcomes.
206
+ // All other existing Eval integrity rules remain authoritative.
207
+ const integrity = evalHistoryRecordIssues(
208
+ run,
209
+ now,
210
+ Math.max(1, now - epoch(plan.window.start)),
211
+ ).filter((reason) => reason !== "execution_not_successful");
212
+ reasons.push(...integrity);
213
+ if (run.label !== plan.commitSha) reasons.push("commit_mismatch");
214
+ if (outcomeDigest(run.comparison) !== sample.comparisonDigest)
215
+ reasons.push("comparison_mismatch");
216
+ if (!(
217
+ epoch(run.ranAt) >= epoch(plan.window.start) &&
218
+ epoch(run.ranAt) <= epoch(plan.window.end)
219
+ ))
220
+ reasons.push("run_outside_window");
221
+ }
222
+ const result = run?.results?.find((item) => item?.id === sample.taskId);
223
+ if (!result && !stoppedBeforeTask) reasons.push("missing_task_result");
224
+ if (result && (!nonnegative(result.ms) || row.elapsedMs < result.ms))
225
+ reasons.push("invalid_elapsed_time");
226
+ if (sample.kind === "first-run") {
227
+ if (typeof observation.firstRun?.cleanEnvironment !== "boolean")
228
+ reasons.push("missing_clean_environment_evidence");
229
+ for (const stage of STAGES) {
230
+ const evidence = observation.firstRun?.stages?.[stage];
231
+ if (
232
+ !evidence ||
233
+ typeof evidence.passed !== "boolean" ||
234
+ !nonempty(evidence.receipt)
235
+ )
236
+ reasons.push(`missing_stage_evidence:${stage}`);
237
+ else if (evidence.passed) row.firstRunCompletedStages.push(stage);
238
+ }
239
+ // A later completed stage without its prerequisites is not a valid funnel.
240
+ if (
241
+ row.firstRunCompletedStages.some(
242
+ (stage, index) => stage !== STAGES[index],
243
+ )
244
+ )
245
+ reasons.push("inconsistent_first_run_stages");
246
+ }
247
+ for (const reason of reasons) issues.push(`${sample.id}:${reason}`);
248
+ const successfulExecution =
249
+ result?.pass === true &&
250
+ result.artifactCheckPassed === true &&
251
+ evalExecutionSucceeded(result) &&
252
+ result.executionEvidence.observedFallback === false;
253
+ if (!successfulExecution) reasons.push("task_not_successful");
254
+ if (row.elapsedMs > sample.timeBudgetMs || row.cost > sample.costBudget)
255
+ reasons.push("budget_exceeded");
256
+ if (row.manualRepairs > 0) reasons.push("manual_repair");
257
+ if (
258
+ sample.kind === "first-run" &&
259
+ (observation.firstRun?.cleanEnvironment !== true ||
260
+ row.firstRunCompletedStages.length !== STAGES.length)
261
+ )
262
+ reasons.push("first_run_not_completed");
263
+ row.succeeded = reasons.length === 0;
264
+ if (!row.succeeded) {
265
+ if (FAILURE_CAUSES.has(observation.failureCause))
266
+ row.failureCause = observation.failureCause;
267
+ else issues.push(`${sample.id}:missing_failure_cause`);
268
+ }
269
+ }
270
+ const categories = Object.fromEntries(
271
+ [...CATEGORIES].map((category) => [category, 0]),
272
+ );
273
+ let maintenanceComplete = false;
274
+ if (maintenance) {
275
+ const entries = uniqueIndex(
276
+ maintenance.entries,
277
+ "id",
278
+ "maintenance entries",
279
+ );
280
+ maintenanceComplete =
281
+ maintenance.complete === true &&
282
+ outcomeDigest(maintenance.window) === outcomeDigest(plan.window);
283
+ for (const entry of entries.values()) {
284
+ assert(
285
+ CATEGORIES.has(entry.category) &&
286
+ nonnegative(entry.hours) &&
287
+ nonempty(entry.evidenceRef),
288
+ "invalid maintenance entry",
289
+ );
290
+ categories[entry.category] += entry.hours;
291
+ }
292
+ }
293
+ if (!maintenanceComplete) issues.push("incomplete_maintenance_records");
294
+ const maintenanceHours = [...CATEGORIES]
295
+ .filter((category) => category !== "construction")
296
+ .reduce((sum, category) => sum + categories[category], 0);
297
+ const attempts = rows.filter((row) => row.observed).length;
298
+ const allObserved = rows.every((row) => row.observed);
299
+ const firstRuns = rows.filter((row) => row.kind === "first-run");
300
+ const tasks = rows.filter((row) => row.kind === "task");
301
+ if (!firstRuns.length || !tasks.length)
302
+ issues.push("missing_metric_population");
303
+ return {
304
+ schema: OUTCOME_REPORT_SCHEMA,
305
+ generatedAt: new Date(now).toISOString(),
306
+ scope: "local-outcome-baseline",
307
+ productionAttested: false,
308
+ status: issues.length ? "INSUFFICIENT_EVIDENCE" : "BASELINE_RECORDED",
309
+ // Neither a baseline nor operator declarations constitute an improvement gate.
310
+ improvementVerdict: "NOT_EVALUATED",
311
+ planDigest,
312
+ commitSha: plan.commitSha,
313
+ currency: plan.currency,
314
+ window: plan.window,
315
+ task: summarize(tasks),
316
+ firstRun: summarize(firstRuns),
317
+ maintenance: {
318
+ complete: maintenanceComplete,
319
+ knownHours: maintenanceHours,
320
+ totalHours: maintenanceComplete ? maintenanceHours : null,
321
+ per100Attempts:
322
+ maintenanceComplete && allObserved && attempts
323
+ ? (maintenanceHours / attempts) * 100
324
+ : null,
325
+ observedAttempts: attempts,
326
+ constructionHours: maintenanceComplete ? categories.construction : null,
327
+ categories,
328
+ },
329
+ groups: [
330
+ ...new Set(
331
+ rows.map((row) => JSON.stringify([row.kind, row.stratum, row.taskId])),
332
+ ),
333
+ ].map((key) => {
334
+ const [kind, stratum, taskId] = JSON.parse(key);
335
+ return {
336
+ kind,
337
+ stratum,
338
+ taskId,
339
+ ...summarize(
340
+ rows.filter(
341
+ (row) =>
342
+ row.kind === kind &&
343
+ row.stratum === stratum &&
344
+ row.taskId === taskId,
345
+ ),
346
+ ),
347
+ };
348
+ }),
349
+ firstRunFunnel: STAGES.map((stage, index) => ({
350
+ stage,
351
+ planned: firstRuns.length,
352
+ passed: firstRuns.filter((row) =>
353
+ STAGES.slice(0, index + 1).every((required) =>
354
+ row.firstRunCompletedStages.includes(required),
355
+ ),
356
+ ).length,
357
+ })),
358
+ failureCauses: [
359
+ ...new Set(
360
+ rows
361
+ .filter((row) => !row.succeeded)
362
+ .map((row) => row.failureCause || "unknown"),
363
+ ),
364
+ ].map((cause) => ({
365
+ cause,
366
+ count: rows.filter(
367
+ (row) => !row.succeeded && (row.failureCause || "unknown") === cause,
368
+ ).length,
369
+ })),
370
+ issues: [...new Set(issues)],
371
+ rows,
372
+ };
373
+ }
@@ -109,6 +109,8 @@ export async function runEvalSuite(tasks, opts = {}) {
109
109
  executionSucceeded: false,
110
110
  agentOk: false,
111
111
  executionEvidence: null,
112
+ usage: null,
113
+ totalCostUsd: null,
112
114
  ms: 0,
113
115
  detail: "",
114
116
  error: null,
@@ -153,6 +155,10 @@ export async function runEvalSuite(tasks, opts = {}) {
153
155
  (agentResult.error == null || agentResult.error === "");
154
156
  rec.agentOk = rec.executionSucceeded;
155
157
  rec.executionEvidence = agentResult?.executionEvidence || null;
158
+ rec.usage = agentResult?.usage || null;
159
+ rec.totalCostUsd = Number.isFinite(agentResult?.totalCostUsd)
160
+ ? agentResult.totalCostUsd
161
+ : null;
156
162
  if (agentResult?.evaluationMetrics !== undefined) {
157
163
  rec.evaluationMetrics = agentResult.evaluationMetrics;
158
164
  }
@@ -0,0 +1,37 @@
1
+ // The git tool accepts argv text, not a shell program. Diagnose shell syntax
2
+ // before spawning git while preserving literal operators in quoted arguments.
3
+ export function gitToolInputError(command) {
4
+ let quote = null;
5
+ for (let i = 0; i < command.length; i++) {
6
+ const char = command[i];
7
+ if (char === "\\" && quote === '"') {
8
+ i++;
9
+ continue;
10
+ }
11
+ if (quote) {
12
+ if (char === quote) quote = null;
13
+ continue;
14
+ }
15
+ if (char === "'" || char === '"') {
16
+ quote = char;
17
+ continue;
18
+ }
19
+ if (/[|&;<>\r\n`]/.test(char) || (char === "$" && command[i + 1] === "(")) {
20
+ return {
21
+ success: false,
22
+ code: "CC_GIT_SHELL_SYNTAX",
23
+ error:
24
+ "The git tool accepts Git arguments only; shell operators are not executed.",
25
+ hint: 'Submit one Git command without pipes, redirects or &&. For example: "merge-base --is-ancestor main HEAD" (inspect exitCode), "log -n 5 --oneline", or "tag --list v-npm-* --sort=-v:refname". Do not reroute to a shell or execute a partial command automatically.',
26
+ };
27
+ }
28
+ }
29
+ if (quote)
30
+ return {
31
+ success: false,
32
+ code: "CC_GIT_UNCLOSED_QUOTE",
33
+ error:
34
+ "Unclosed quote in Git arguments. Close the quoted argument and retry.",
35
+ };
36
+ return null;
37
+ }
@@ -55,6 +55,7 @@ export async function reconcileConfigLlmProvider(cfgLlm = {}) {
55
55
  }
56
56
 
57
57
  import { resolveApiKeyFromHelper } from "./api-key-helper.js";
58
+ import { modelForeignToProvider } from "./runnable-provider.js";
58
59
 
59
60
  /**
60
61
  * Key backfill order: config llm.apiKey (plaintext, legacy) first, then
@@ -111,12 +112,27 @@ function optionalModel(value) {
111
112
  return model || null;
112
113
  }
113
114
 
115
+ /**
116
+ * Claude settings and ANTHROPIC_MODEL carry a model name but no provider.
117
+ * Do not pair an unmistakably provider-specific inherited model with another
118
+ * configured provider. Explicit CLI/--settings choices may opt out because
119
+ * they provide the user's intent for this invocation.
120
+ */
121
+ export function inheritedModelCompatible(provider, model) {
122
+ const candidate = optionalModel(model);
123
+ return (
124
+ !candidate || !provider || !modelForeignToProvider(provider, candidate)
125
+ );
126
+ }
127
+
114
128
  /**
115
129
  * Resolve the model precedence used by `cc agent` without allowing a default
116
130
  * from one source to silently outrank an explicit choice from another:
117
131
  *
118
- * CLI --model > ANTHROPIC_MODEL > settings.model > config/organization model
119
- * > ANTHROPIC_DEFAULT_MODEL (new sessions only) > runtime provider default.
132
+ * CLI --model > provider-compatible ANTHROPIC_MODEL/settings.model >
133
+ * config/organization model > ANTHROPIC_DEFAULT_MODEL (new sessions only)
134
+ * > runtime provider default. An explicit --settings file or managed setting
135
+ * may authorize a foreign-looking alias for a custom provider endpoint.
120
136
  *
121
137
  * Environment values are supplied by the caller from the process startup
122
138
  * environment, before settings-file `env` entries are merged. That prevents a
@@ -128,18 +144,25 @@ export function applyAgentModelDefaults(options = {}, cfgLlm = {}, opts = {}) {
128
144
  const settingsModel = optionalModel(opts.settingsModel);
129
145
  const defaultModel = optionalModel(opts.anthropicDefaultModel);
130
146
 
131
- if (!explicitModel) {
132
- if (environmentModel) {
133
- options.model = environmentModel;
134
- } else if (settingsModel) {
135
- options.model = settingsModel;
136
- }
147
+ let inheritedModel = environmentModel || settingsModel;
148
+ const targetProvider = options.provider || cfgLlm.provider;
149
+ if (
150
+ inheritedModel &&
151
+ opts.allowForeignInheritedModel !== true &&
152
+ !inheritedModelCompatible(targetProvider, inheritedModel)
153
+ ) {
154
+ if (options.model === inheritedModel) delete options.model;
155
+ inheritedModel = null;
156
+ }
157
+
158
+ if (!explicitModel && inheritedModel) {
159
+ options.model = inheritedModel;
137
160
  }
138
161
 
139
162
  applyConfigLlmDefaults(options, cfgLlm, {
140
163
  // Reuse the existing config/provider wiring, but do not let its configured
141
164
  // model overwrite one selected by a higher-priority source above.
142
- explicitModel: explicitModel || environmentModel || settingsModel,
165
+ explicitModel: explicitModel || inheritedModel,
143
166
  });
144
167
 
145
168
  if (!options.model && opts.isNewSession !== false && defaultModel) {
@@ -30,6 +30,46 @@ export class ReadFileLoopGuard {
30
30
  this.recoveryOfferedAt = 0;
31
31
  this.batch = null;
32
32
  this.largeOutputs = new Set();
33
+ this.editRecoveryReads = new Map();
34
+ }
35
+
36
+ recordEditRecovery(result) {
37
+ if (result?.success === true && result.path) {
38
+ this.editRecoveryReads.delete(keyFor(result.path, { hashed: true }));
39
+ return;
40
+ }
41
+ const request = result?.recoveryRead;
42
+ if (
43
+ !request ||
44
+ !["hash_mismatch", "content_mismatch"].includes(result.error)
45
+ )
46
+ return;
47
+ const key = keyFor(request.path, { hashed: true });
48
+ // One refresh per file until an edit succeeds. Repeating the failed edit
49
+ // must not mint an unlimited read allowance.
50
+ if (
51
+ !this.editRecoveryReads.has(key) &&
52
+ this.editRecoveryReads.size < MAX_FILES
53
+ ) {
54
+ this.editRecoveryReads.set(key, { ...request, used: false });
55
+ }
56
+ }
57
+
58
+ get hasEditRecoveryReads() {
59
+ return [...this.editRecoveryReads.values()].some((entry) => !entry.used);
60
+ }
61
+
62
+ canRecoverEdit(filePath, args = {}) {
63
+ const entry = this.editRecoveryReads.get(keyFor(filePath, args));
64
+ return !!(
65
+ entry &&
66
+ !entry.used &&
67
+ args.hashed === true &&
68
+ args.raw !== true &&
69
+ args.column == null &&
70
+ args.offset === entry.offset &&
71
+ args.limit === entry.limit
72
+ );
33
73
  }
34
74
 
35
75
  startBatch() {
@@ -51,6 +91,16 @@ export class ReadFileLoopGuard {
51
91
  }
52
92
  let page = readPage(args);
53
93
  if (page.error || !page.readSpan) return page;
94
+ if (this.canRecoverEdit(filePath, args)) {
95
+ this.editRecoveryReads.get(key).used = true;
96
+ // The authorized read implementation has already refreshed stat/content.
97
+ // Return the requested anchors even if ordinary coverage is exhausted.
98
+ return {
99
+ ...page,
100
+ readRecovery: { action: "edit-anchor-refresh" },
101
+ readProgress: { newContent: false },
102
+ };
103
+ }
54
104
  const span = page.readSpan;
55
105
  const covered = entry.spans.some(
56
106
  ([start, end]) => start <= span.start && end >= span.end,
@@ -160,6 +210,8 @@ export class ReadFileLoopGuard {
160
210
  )
161
211
  return;
162
212
  if (tool === "read_file") {
213
+ if (result?.readRecovery?.action === "edit-anchor-refresh")
214
+ this.batch.advanced = true;
163
215
  if (result?.readProgress?.newContent === true) this.batch.advanced = true;
164
216
  if (result?.readProgress?.newContent === false) this.batch.duplicates++;
165
217
  } else if (
@@ -207,12 +259,14 @@ export class ReadFileLoopGuard {
207
259
 
208
260
  get hasRecoveryReads() {
209
261
  return (
262
+ this.hasEditRecoveryReads ||
210
263
  this.hasUnreadPages ||
211
264
  [...this.progress.values()].some((entry) => entry.rereads.size < 3)
212
265
  );
213
266
  }
214
267
 
215
268
  canContinue(filePath, args = {}) {
269
+ if (this.canRecoverEdit(filePath, args)) return true;
216
270
  const entry = this.progress.get(keyFor(filePath, args));
217
271
  return (
218
272
  entry?.summary?.reachedEnd === false ||
@@ -1,4 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { diagnosticExcerpt } from "./diagnostic-excerpt.js";
3
+ import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "./diagnostic-workflow.js";
2
4
 
3
5
  const MAX_TARGETS = 32;
4
6
  const RECOVERY_AFTER = 3;
@@ -196,19 +198,7 @@ function failedResult(result) {
196
198
  }
197
199
 
198
200
  function excerpt(value) {
199
- const text = typeof value === "string" ? value : JSON.stringify(value ?? "");
200
- if (text.length <= 1600) return text;
201
- // Retain failure lines from full CI logs as well as the opening context.
202
- const failures = text
203
- .split("\n")
204
- .filter((line) => /FAIL|Error:|AssertionError|error TS|×|✕/.test(line))
205
- .slice(0, 12)
206
- .join("\n");
207
- return (
208
- text.slice(0, 400) +
209
- "\n[excerpt]\n" +
210
- (failures || text.slice(-1100)).slice(0, 1100)
211
- );
201
+ return diagnosticExcerpt(value);
212
202
  }
213
203
 
214
204
  /** Per agentLoop, independent of history compaction and other agents' evidence. */
@@ -290,13 +280,19 @@ export class RemoteReadLoopGuard {
290
280
  this.activeKey = target.key;
291
281
  const previous = this.targets.get(target.key);
292
282
  const digests = previous?.digests || new Set();
293
- const content =
283
+ const primaryContent =
294
284
  result?.output ??
295
285
  result?.stdout ??
296
286
  result?.content ??
297
287
  result?.matches ??
298
288
  result?.body ??
299
289
  "";
290
+ const content =
291
+ result?.stdout_diagnostics || result?.stderr_diagnostics
292
+ ? [result.stdout_diagnostics, result.stderr_diagnostics, primaryContent]
293
+ .filter(Boolean)
294
+ .join("\n")
295
+ : primaryContent;
300
296
  const digest = createHash("sha256")
301
297
  .update(typeof content === "string" ? content : JSON.stringify(content))
302
298
  .digest("hex");
@@ -453,6 +449,8 @@ export class RemoteReadLoopGuard {
453
449
  "An aggregate or incomplete-matrix gate can be a downstream symptom; inspect the dependency jobs' conclusions before assuming that the gate itself is wrong. " +
454
450
  "When a job is cancelled or logs are unavailable, record that fact and the command error; do not cycle through the same web page and log downloads. " +
455
451
  "Use one focused run/job metadata query to resolve the missing status or cancellation reason, then inspect the relevant workflow and make the justified, authorized fix with validation. " +
452
+ DIAGNOSTIC_WORKFLOW_GUIDANCE +
453
+ " " +
456
454
  "If available evidence cannot establish a fix, report the specific missing evidence and what was verified. Do not disable a release or safety gate just to make CI pass. " +
457
455
  "For a research/review request, synthesize the findings without unsolicited edits; for monitoring, use status queries instead of repeated logs."
458
456
  );