@autobe/agent 0.30.0-dev.20260315 → 0.30.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 (466) hide show
  1. package/LICENSE +661 -661
  2. package/lib/constants/AutoBeTemplateFileConstant.d.ts +1 -1
  3. package/lib/factory/consentFunctionCall.js +4 -4
  4. package/lib/factory/createAutoBeContext.js +24 -24
  5. package/lib/factory/createAutoBeMessageContent.js +6 -6
  6. package/lib/index.mjs +41 -41
  7. package/lib/orchestrate/analyze/histories/transformAnalyzeScenarioHistory.js +18 -18
  8. package/lib/orchestrate/analyze/histories/transformAnalyzeScenarioReviewHistory.js +13 -13
  9. package/lib/orchestrate/analyze/histories/transformAnalyzeSectionCrossFileReviewHistory.js +47 -47
  10. package/lib/orchestrate/analyze/histories/transformAnalyzeSectionReviewHistory.js +66 -66
  11. package/lib/orchestrate/analyze/histories/transformAnalyzeWriteSectionHistory.js +91 -91
  12. package/lib/orchestrate/analyze/histories/transformAnalyzeWriteSectionPatchHistory.js +46 -46
  13. package/lib/orchestrate/analyze/histories/transformAnalyzeWriteUnitHistory.js +72 -72
  14. package/lib/orchestrate/analyze/orchestrateAnalyzeScenario.js +1 -1
  15. package/lib/orchestrate/analyze/orchestrateAnalyzeSectionCrossFileReview.js +1 -1
  16. package/lib/orchestrate/analyze/orchestrateAnalyzeSectionReview.js +1 -1
  17. package/lib/orchestrate/analyze/orchestrateAnalyzeWriteSection.js +1 -1
  18. package/lib/orchestrate/analyze/orchestrateAnalyzeWriteSectionPatch.js +1 -1
  19. package/lib/orchestrate/analyze/orchestrateAnalyzeWriteUnit.js +1 -1
  20. package/lib/orchestrate/common/histories/transformCommonCorrectCastingHistory.js +5 -5
  21. package/lib/orchestrate/common/histories/transformPreliminaryHistory.js +44 -44
  22. package/lib/orchestrate/common/histories/transformPreviousAndLatestCorrectHistory.js +44 -44
  23. package/lib/orchestrate/common/internal/fixPrelminaryApplication.js +22 -22
  24. package/lib/orchestrate/common/internal/validatePreliminary.js +116 -116
  25. package/lib/orchestrate/facade/createAutoBeFacadeController.js +3 -3
  26. package/lib/orchestrate/facade/structures/transformFacadeStateMessage.js +20 -20
  27. package/lib/orchestrate/interface/histories/transformInterfaceActionEndpointReviewHistory.js +34 -34
  28. package/lib/orchestrate/interface/histories/transformInterfaceActionEndpointWriteHistory.js +37 -37
  29. package/lib/orchestrate/interface/histories/transformInterfaceAuthorizationHistory.js +56 -56
  30. package/lib/orchestrate/interface/histories/transformInterfaceBaseEndpointReviewHistory.js +26 -26
  31. package/lib/orchestrate/interface/histories/transformInterfaceBaseEndpointWriteHistory.js +28 -28
  32. package/lib/orchestrate/interface/histories/transformInterfaceEndpointAuthorizationSection.js +11 -11
  33. package/lib/orchestrate/interface/histories/transformInterfaceGroupHistory.js +38 -38
  34. package/lib/orchestrate/interface/histories/transformInterfaceOperationHistory.js +29 -29
  35. package/lib/orchestrate/interface/histories/transformInterfaceOperationParameterHistory.js +15 -15
  36. package/lib/orchestrate/interface/histories/transformInterfaceOperationReviewHistory.js +6 -6
  37. package/lib/orchestrate/interface/histories/transformInterfacePrerequisiteHistory.js +25 -25
  38. package/lib/orchestrate/interface/histories/transformInterfaceSchemaCastingHistory.js +67 -67
  39. package/lib/orchestrate/interface/histories/transformInterfaceSchemaComplementHistory.js +60 -60
  40. package/lib/orchestrate/interface/histories/transformInterfaceSchemaRefineHistory.js +83 -83
  41. package/lib/orchestrate/interface/histories/transformInterfaceSchemaRenameHistory.js +29 -29
  42. package/lib/orchestrate/interface/histories/transformInterfaceSchemaReviewHistory.js +74 -74
  43. package/lib/orchestrate/interface/histories/transformInterfaceSchemaWriteHistory.js +46 -46
  44. package/lib/orchestrate/interface/orchestrateInterfaceAuthorization.js +1 -1
  45. package/lib/orchestrate/interface/orchestrateInterfaceEndpointReview.js +1 -1
  46. package/lib/orchestrate/interface/orchestrateInterfaceEndpointWrite.js +1 -1
  47. package/lib/orchestrate/interface/orchestrateInterfaceGroup.js +9 -9
  48. package/lib/orchestrate/interface/orchestrateInterfaceOperation.js +1 -1
  49. package/lib/orchestrate/interface/orchestrateInterfaceOperationReview.js +1 -1
  50. package/lib/orchestrate/interface/orchestrateInterfacePrerequisite.js +4 -4
  51. package/lib/orchestrate/interface/orchestrateInterfaceSchemaCasting.js +1 -1
  52. package/lib/orchestrate/interface/orchestrateInterfaceSchemaComplement.js +5 -5
  53. package/lib/orchestrate/interface/orchestrateInterfaceSchemaRefine.js +1 -1
  54. package/lib/orchestrate/interface/orchestrateInterfaceSchemaReview.js +1 -1
  55. package/lib/orchestrate/interface/orchestrateInterfaceSchemaWrite.js +1 -1
  56. package/lib/orchestrate/interface/programmers/AutoBeInterfaceAuthorizationProgrammer.js +90 -90
  57. package/lib/orchestrate/interface/programmers/AutoBeInterfaceEndpointProgrammer.js +6 -6
  58. package/lib/orchestrate/interface/programmers/AutoBeInterfaceEndpointReviewProgrammer.js +25 -25
  59. package/lib/orchestrate/interface/programmers/AutoBeInterfaceOperationProgrammer.js +71 -71
  60. package/lib/orchestrate/interface/programmers/AutoBeInterfacePrerequisiteProgrammer.js +162 -162
  61. package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaProgrammer.js +11 -11
  62. package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaPropertyReviseProgrammer.js +73 -73
  63. package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaRefineProgrammer.js +35 -35
  64. package/lib/orchestrate/interface/programmers/AutoBeInterfaceSchemaReviewProgrammer.js +11 -11
  65. package/lib/orchestrate/interface/utils/AutoBeJsonSchemaFactory.js +4 -4
  66. package/lib/orchestrate/interface/utils/AutoBeJsonSchemaValidator.js +283 -283
  67. package/lib/orchestrate/interface/utils/fulfillJsonSchemaErrorMessages.js +76 -76
  68. package/lib/orchestrate/prisma/histories/transformPrismaAuthorizationHistory.js +76 -76
  69. package/lib/orchestrate/prisma/histories/transformPrismaAuthorizationReviewHistory.js +51 -51
  70. package/lib/orchestrate/prisma/histories/transformPrismaComponentReviewHistory.js +54 -54
  71. package/lib/orchestrate/prisma/histories/transformPrismaComponentsHistory.js +83 -83
  72. package/lib/orchestrate/prisma/histories/transformPrismaCorrectHistory.js +6 -6
  73. package/lib/orchestrate/prisma/histories/transformPrismaGroupHistory.js +22 -22
  74. package/lib/orchestrate/prisma/histories/transformPrismaGroupReviewHistory.js +41 -41
  75. package/lib/orchestrate/prisma/histories/transformPrismaSchemaHistory.js +39 -39
  76. package/lib/orchestrate/prisma/histories/transformPrismaSchemaReviewHistory.js +41 -41
  77. package/lib/orchestrate/prisma/orchestratePrismaAuthorization.js +1 -1
  78. package/lib/orchestrate/prisma/orchestratePrismaAuthorizationReview.js +1 -1
  79. package/lib/orchestrate/prisma/orchestratePrismaComponent.js +1 -1
  80. package/lib/orchestrate/prisma/orchestratePrismaComponentReview.js +1 -1
  81. package/lib/orchestrate/prisma/orchestratePrismaCorrect.js +1 -1
  82. package/lib/orchestrate/prisma/orchestratePrismaGroup.js +1 -1
  83. package/lib/orchestrate/prisma/orchestratePrismaGroupReview.js +1 -1
  84. package/lib/orchestrate/prisma/orchestratePrismaSchema.js +1 -1
  85. package/lib/orchestrate/prisma/orchestratePrismaSchemaReview.js +1 -1
  86. package/lib/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationProgrammer.js +23 -23
  87. package/lib/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationReviewProgrammer.js +7 -7
  88. package/lib/orchestrate/prisma/programmers/AutoBeDatabaseComponentProgrammer.js +5 -5
  89. package/lib/orchestrate/prisma/programmers/AutoBeDatabaseComponentReviewProgrammer.js +20 -20
  90. package/lib/orchestrate/prisma/programmers/AutoBeDatabaseGroupProgrammer.js +13 -13
  91. package/lib/orchestrate/prisma/programmers/AutoBeDatabaseGroupReviewProgrammer.js +51 -51
  92. package/lib/orchestrate/prisma/programmers/AutoBeDatabaseSchemaProgrammer.js +25 -25
  93. package/lib/orchestrate/realize/histories/transformRealizeAuthorizationCorrectHistory.js +28 -28
  94. package/lib/orchestrate/realize/histories/transformRealizeAuthorizationWriteHistory.js +14 -14
  95. package/lib/orchestrate/realize/histories/transformRealizeCollectorCorrectHistory.js +42 -42
  96. package/lib/orchestrate/realize/histories/transformRealizeCollectorPlanHistory.js +27 -27
  97. package/lib/orchestrate/realize/histories/transformRealizeCollectorWriteHistory.js +57 -57
  98. package/lib/orchestrate/realize/histories/transformRealizeCorrectCastingHistory.js +35 -35
  99. package/lib/orchestrate/realize/histories/transformRealizeOperationCorrectHistory.js +14 -14
  100. package/lib/orchestrate/realize/histories/transformRealizeOperationWriteHistory.js +37 -37
  101. package/lib/orchestrate/realize/histories/transformRealizeOperationWriteHistory.js.map +1 -1
  102. package/lib/orchestrate/realize/histories/transformRealizeTransformerCorrectHistory.js +47 -47
  103. package/lib/orchestrate/realize/histories/transformRealizeTransformerPlanHistory.js +27 -27
  104. package/lib/orchestrate/realize/histories/transformRealizeTransformerWriteHistory.js +51 -51
  105. package/lib/orchestrate/realize/orchestrateRealizeAuthorizationCorrect.js +1 -1
  106. package/lib/orchestrate/realize/orchestrateRealizeAuthorizationWrite.js +1 -1
  107. package/lib/orchestrate/realize/orchestrateRealizeCollectorCorrectOverall.js +1 -1
  108. package/lib/orchestrate/realize/orchestrateRealizeCollectorPlan.js +11 -11
  109. package/lib/orchestrate/realize/orchestrateRealizeCollectorWrite.js +1 -1
  110. package/lib/orchestrate/realize/orchestrateRealizeOperationCorrectOverall.js +1 -1
  111. package/lib/orchestrate/realize/orchestrateRealizeOperationWrite.js +1 -1
  112. package/lib/orchestrate/realize/orchestrateRealizeTransformerCorrectOverall.js +1 -1
  113. package/lib/orchestrate/realize/orchestrateRealizeTransformerPlan.js +11 -11
  114. package/lib/orchestrate/realize/orchestrateRealizeTransformerWrite.js +1 -1
  115. package/lib/orchestrate/realize/programmers/AutoBeRealizeCollectorProgrammer.js +41 -41
  116. package/lib/orchestrate/realize/programmers/AutoBeRealizeOperationProgrammer.js +21 -21
  117. package/lib/orchestrate/realize/programmers/AutoBeRealizeTransformerProgrammer.js +57 -57
  118. package/lib/orchestrate/realize/utils/getRealizeWriteInputType.js +3 -3
  119. package/lib/orchestrate/realize/utils/printErrorHints.js +5 -5
  120. package/lib/orchestrate/test/compile/getTestImportStatements.js +9 -9
  121. package/lib/orchestrate/test/histories/transformTestAuthorizeWriteHistory.js +38 -38
  122. package/lib/orchestrate/test/histories/transformTestGenerationWriteHistory.js +79 -79
  123. package/lib/orchestrate/test/histories/transformTestOperationWriteHistory.js +162 -162
  124. package/lib/orchestrate/test/histories/transformTestPrepareWriteHistory.js +54 -54
  125. package/lib/orchestrate/test/histories/transformTestScenarioHistory.js +31 -31
  126. package/lib/orchestrate/test/histories/transformTestScenarioReviewHistory.js +27 -27
  127. package/lib/orchestrate/test/orchestrateTestScenario.js +1 -1
  128. package/lib/orchestrate/test/orchestrateTestScenarioReview.js +1 -1
  129. package/lib/orchestrate/test/programmers/AutoBeTestAuthorizeProgrammer.js +31 -31
  130. package/lib/orchestrate/test/programmers/AutoBeTestGenerateProgrammer.js +23 -23
  131. package/lib/orchestrate/test/programmers/AutoBeTestOperationProgrammer.js +6 -6
  132. package/lib/orchestrate/test/programmers/AutoBeTestPrepareProgrammer.js +34 -34
  133. package/lib/orchestrate/test/programmers/AutoBeTestScenarioProgrammer.js +39 -39
  134. package/lib/utils/predicateStateMessage.js +19 -19
  135. package/lib/utils/validateEmptyCode.js +16 -16
  136. package/package.json +5 -5
  137. package/src/AutoBeAgent.ts +374 -374
  138. package/src/AutoBeAgentBase.ts +126 -126
  139. package/src/AutoBeMockAgent.ts +254 -254
  140. package/src/constants/AutoBeConfigConstant.ts +173 -173
  141. package/src/constants/AutoBeTemplateFileConstant.ts +1 -1
  142. package/src/context/AutoBeContext.ts +107 -107
  143. package/src/context/AutoBeState.ts +66 -66
  144. package/src/context/AutoBeTokenUsage.ts +307 -307
  145. package/src/context/AutoBeTokenUsageComponent.ts +227 -227
  146. package/src/describe/describe.ts +47 -47
  147. package/src/describe/image/histories/transformImageDescribeDraftHistories.ts +16 -16
  148. package/src/describe/image/orchestrateImageDescribeDraft.ts +200 -200
  149. package/src/describe/image/structures/IAutoBeImageDescribeDraftApplication.ts +92 -92
  150. package/src/describe/imageDescribe.ts +64 -64
  151. package/src/factory/consentFunctionCall.ts +141 -141
  152. package/src/factory/createAgenticaHistory.ts +71 -71
  153. package/src/factory/createAutoBeContext.ts +584 -584
  154. package/src/factory/createAutoBeMessageContent.ts +36 -36
  155. package/src/factory/createAutoBeState.ts +20 -20
  156. package/src/factory/getAutoBeGenerated.ts +317 -317
  157. package/src/factory/getAutoBeRealizeGenerated.ts +31 -31
  158. package/src/factory/getCriticalCompiler.ts +52 -52
  159. package/src/factory/index.ts +1 -1
  160. package/src/factory/mergeSystemMessages.ts +60 -60
  161. package/src/factory/supportFunctionCallFallback.ts +214 -214
  162. package/src/factory/supportMistral.ts +138 -138
  163. package/src/index.ts +16 -16
  164. package/src/orchestrate/analyze/fillTocDeterministic.ts +280 -280
  165. package/src/orchestrate/analyze/histories/transformAnalyzeScenarioHistory.ts +55 -55
  166. package/src/orchestrate/analyze/histories/transformAnalyzeScenarioReviewHistory.ts +74 -74
  167. package/src/orchestrate/analyze/histories/transformAnalyzeSectionCrossFileReviewHistory.ts +179 -179
  168. package/src/orchestrate/analyze/histories/transformAnalyzeSectionReviewHistory.ts +163 -163
  169. package/src/orchestrate/analyze/histories/transformAnalyzeWriteSectionHistory.ts +191 -191
  170. package/src/orchestrate/analyze/histories/transformAnalyzeWriteSectionPatchHistory.ts +122 -122
  171. package/src/orchestrate/analyze/histories/transformAnalyzeWriteUnitHistory.ts +161 -161
  172. package/src/orchestrate/analyze/index.ts +7 -7
  173. package/src/orchestrate/analyze/orchestrateAnalyze.ts +1693 -1693
  174. package/src/orchestrate/analyze/orchestrateAnalyzeScenario.ts +267 -267
  175. package/src/orchestrate/analyze/orchestrateAnalyzeScenarioReview.ts +122 -122
  176. package/src/orchestrate/analyze/orchestrateAnalyzeSectionCrossFileReview.ts +146 -146
  177. package/src/orchestrate/analyze/orchestrateAnalyzeSectionReview.ts +154 -154
  178. package/src/orchestrate/analyze/orchestrateAnalyzeWriteSection.ts +388 -388
  179. package/src/orchestrate/analyze/orchestrateAnalyzeWriteSectionPatch.ts +407 -407
  180. package/src/orchestrate/analyze/orchestrateAnalyzeWriteUnit.ts +348 -348
  181. package/src/orchestrate/analyze/programmers/AutoBeAnalyzeProgrammer.ts +149 -149
  182. package/src/orchestrate/analyze/structures/FixedAnalyzeTemplate.ts +961 -961
  183. package/src/orchestrate/analyze/structures/IAutoBeAnalyzeScenarioApplication.ts +141 -141
  184. package/src/orchestrate/analyze/structures/IAutoBeAnalyzeScenarioReviewApplication.ts +96 -96
  185. package/src/orchestrate/analyze/structures/IAutoBeAnalyzeSectionCrossFileReviewApplication.ts +160 -160
  186. package/src/orchestrate/analyze/structures/IAutoBeAnalyzeSectionReviewApplication.ts +192 -192
  187. package/src/orchestrate/analyze/structures/IAutoBeAnalyzeWriteSectionApplication.ts +142 -142
  188. package/src/orchestrate/analyze/structures/IAutoBeAnalyzeWriteUnitApplication.ts +123 -123
  189. package/src/orchestrate/analyze/utils/buildConstraintConsistencyReport.ts +813 -813
  190. package/src/orchestrate/analyze/utils/buildErrorCodeRegistry.ts +324 -324
  191. package/src/orchestrate/analyze/utils/buildHardValidators.ts +88 -88
  192. package/src/orchestrate/analyze/utils/detectInventedEntities.ts +87 -87
  193. package/src/orchestrate/analyze/utils/detectProseConstraintConflicts.ts +319 -319
  194. package/src/orchestrate/analyze/utils/repairSectionReviewUtils.ts +181 -181
  195. package/src/orchestrate/analyze/utils/repairUtils.ts +60 -60
  196. package/src/orchestrate/analyze/utils/validateScenarioBasics.ts +104 -104
  197. package/src/orchestrate/common/AutoBePreliminaryController.ts +400 -400
  198. package/src/orchestrate/common/histories/transformCommonCorrectCastingHistory.ts +32 -32
  199. package/src/orchestrate/common/histories/transformPreliminaryHistory.ts +742 -742
  200. package/src/orchestrate/common/histories/transformPreviousAndLatestCorrectHistory.ts +77 -77
  201. package/src/orchestrate/common/internal/complementPreliminaryCollection.ts +263 -263
  202. package/src/orchestrate/common/internal/convertToSectionEntries.ts +57 -57
  203. package/src/orchestrate/common/internal/createPreliminaryCollection.ts +76 -76
  204. package/src/orchestrate/common/internal/fixPrelminaryApplication.ts +346 -346
  205. package/src/orchestrate/common/internal/validatePreliminary.ts +671 -671
  206. package/src/orchestrate/common/orchestrateCommonCorrectCasting.ts +223 -223
  207. package/src/orchestrate/common/orchestratePreliminary.ts +610 -610
  208. package/src/orchestrate/common/structures/AutoBePreliminaryRequest.ts +42 -42
  209. package/src/orchestrate/common/structures/IAnalysisSectionEntry.ts +35 -35
  210. package/src/orchestrate/common/structures/IAutoBeCommonCorrectCastingApplication.ts +72 -72
  211. package/src/orchestrate/common/structures/IAutoBeOrchestrateResult.ts +28 -28
  212. package/src/orchestrate/common/structures/IAutoBePreliminaryCollection.ts +52 -52
  213. package/src/orchestrate/common/structures/IAutoBePreliminaryGetAnalysisSections.ts +34 -34
  214. package/src/orchestrate/common/structures/IAutoBePreliminaryGetDatabaseSchemas.ts +31 -31
  215. package/src/orchestrate/common/structures/IAutoBePreliminaryGetInterfaceOperations.ts +32 -32
  216. package/src/orchestrate/common/structures/IAutoBePreliminaryGetInterfaceSchemas.ts +31 -31
  217. package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousAnalysisSections.ts +28 -28
  218. package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousDatabaseSchemas.ts +88 -88
  219. package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousInterfaceOperations.ts +102 -102
  220. package/src/orchestrate/common/structures/IAutoBePreliminaryGetPreviousInterfaceSchemas.ts +105 -105
  221. package/src/orchestrate/common/structures/IAutoBePreliminaryGetRealizeCollectors.ts +32 -32
  222. package/src/orchestrate/common/structures/IAutoBePreliminaryGetRealizeTransformers.ts +32 -32
  223. package/src/orchestrate/facade/createAutoBeFacadeController.ts +118 -118
  224. package/src/orchestrate/facade/histories/IAutoBeFacadeApplication.ts +139 -139
  225. package/src/orchestrate/facade/histories/IAutoBeFacadeApplicationProps.ts +8 -8
  226. package/src/orchestrate/facade/histories/IAutoBeFacadeApplicationResult.ts +9 -9
  227. package/src/orchestrate/facade/structures/transformFacadeStateMessage.ts +59 -59
  228. package/src/orchestrate/index.ts +5 -5
  229. package/src/orchestrate/interface/histories/transformInterfaceActionEndpointReviewHistory.ts +83 -83
  230. package/src/orchestrate/interface/histories/transformInterfaceActionEndpointWriteHistory.ts +78 -78
  231. package/src/orchestrate/interface/histories/transformInterfaceAuthorizationHistory.ts +129 -129
  232. package/src/orchestrate/interface/histories/transformInterfaceBaseEndpointReviewHistory.ts +74 -74
  233. package/src/orchestrate/interface/histories/transformInterfaceBaseEndpointWriteHistory.ts +68 -68
  234. package/src/orchestrate/interface/histories/transformInterfaceCommonHistory.ts +64 -64
  235. package/src/orchestrate/interface/histories/transformInterfaceEndpointAuthorizationSection.ts +42 -42
  236. package/src/orchestrate/interface/histories/transformInterfaceGroupHistory.ts +103 -103
  237. package/src/orchestrate/interface/histories/transformInterfaceOperationHistory.ts +81 -81
  238. package/src/orchestrate/interface/histories/transformInterfaceOperationParameterHistory.ts +53 -53
  239. package/src/orchestrate/interface/histories/transformInterfaceOperationReviewHistory.ts +49 -49
  240. package/src/orchestrate/interface/histories/transformInterfacePrerequisiteHistory.ts +94 -94
  241. package/src/orchestrate/interface/histories/transformInterfaceSchemaCastingHistory.ts +122 -122
  242. package/src/orchestrate/interface/histories/transformInterfaceSchemaComplementHistory.ts +192 -192
  243. package/src/orchestrate/interface/histories/transformInterfaceSchemaRefineHistory.ts +189 -189
  244. package/src/orchestrate/interface/histories/transformInterfaceSchemaRenameHistory.ts +63 -63
  245. package/src/orchestrate/interface/histories/transformInterfaceSchemaReviewHistory.ts +173 -173
  246. package/src/orchestrate/interface/histories/transformInterfaceSchemaWriteHistory.ts +88 -88
  247. package/src/orchestrate/interface/index.ts +6 -6
  248. package/src/orchestrate/interface/orchestrateInterface.ts +118 -118
  249. package/src/orchestrate/interface/orchestrateInterfaceActionEndpoint.ts +58 -58
  250. package/src/orchestrate/interface/orchestrateInterfaceAuthorization.ts +208 -208
  251. package/src/orchestrate/interface/orchestrateInterfaceBaseEndpoint.ts +55 -55
  252. package/src/orchestrate/interface/orchestrateInterfaceEndpoint.ts +67 -67
  253. package/src/orchestrate/interface/orchestrateInterfaceEndpointOverall.ts +80 -80
  254. package/src/orchestrate/interface/orchestrateInterfaceEndpointReview.ts +212 -212
  255. package/src/orchestrate/interface/orchestrateInterfaceEndpointWrite.ts +236 -236
  256. package/src/orchestrate/interface/orchestrateInterfaceGroup.ts +166 -166
  257. package/src/orchestrate/interface/orchestrateInterfaceOperation.ts +322 -322
  258. package/src/orchestrate/interface/orchestrateInterfaceOperationReview.ts +245 -245
  259. package/src/orchestrate/interface/orchestrateInterfacePrerequisite.ts +240 -240
  260. package/src/orchestrate/interface/orchestrateInterfaceSchema.ts +191 -191
  261. package/src/orchestrate/interface/orchestrateInterfaceSchemaCasting.ts +274 -274
  262. package/src/orchestrate/interface/orchestrateInterfaceSchemaComplement.ts +329 -329
  263. package/src/orchestrate/interface/orchestrateInterfaceSchemaRefine.ts +292 -292
  264. package/src/orchestrate/interface/orchestrateInterfaceSchemaRename.ts +266 -266
  265. package/src/orchestrate/interface/orchestrateInterfaceSchemaReview.ts +310 -310
  266. package/src/orchestrate/interface/orchestrateInterfaceSchemaWrite.ts +283 -283
  267. package/src/orchestrate/interface/programmers/AutoBeInterfaceAuthorizationProgrammer.ts +260 -260
  268. package/src/orchestrate/interface/programmers/AutoBeInterfaceEndpointProgrammer.ts +155 -155
  269. package/src/orchestrate/interface/programmers/AutoBeInterfaceEndpointReviewProgrammer.ts +139 -139
  270. package/src/orchestrate/interface/programmers/AutoBeInterfaceOperationProgrammer.ts +277 -277
  271. package/src/orchestrate/interface/programmers/AutoBeInterfacePrerequisiteProgrammer.ts +259 -259
  272. package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaProgrammer.ts +152 -152
  273. package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaPropertyReviseProgrammer.ts +347 -347
  274. package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaRefineProgrammer.ts +183 -183
  275. package/src/orchestrate/interface/programmers/AutoBeInterfaceSchemaReviewProgrammer.ts +263 -263
  276. package/src/orchestrate/interface/structures/IAutoBeInterfaceAuthorizationApplication.ts +135 -135
  277. package/src/orchestrate/interface/structures/IAutoBeInterfaceEndpointReviewApplication.ts +128 -128
  278. package/src/orchestrate/interface/structures/IAutoBeInterfaceEndpointWriteApplication.ts +133 -133
  279. package/src/orchestrate/interface/structures/IAutoBeInterfaceGroupApplication.ts +122 -122
  280. package/src/orchestrate/interface/structures/IAutoBeInterfaceOperationApplication.ts +178 -178
  281. package/src/orchestrate/interface/structures/IAutoBeInterfaceOperationReviewApplication.ts +175 -175
  282. package/src/orchestrate/interface/structures/IAutoBeInterfacePrerequisiteApplication.ts +130 -130
  283. package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaApplication.ts +121 -121
  284. package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaCastingApplication.ts +153 -153
  285. package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaComplementApplication.ts +123 -123
  286. package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaRefineApplication.ts +181 -181
  287. package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaRenameApplication.ts +45 -45
  288. package/src/orchestrate/interface/structures/IAutoBeInterfaceSchemaReviewApplication.ts +108 -108
  289. package/src/orchestrate/interface/utils/AutoBeJsonSchemaCollection.ts +42 -42
  290. package/src/orchestrate/interface/utils/AutoBeJsonSchemaFactory.ts +714 -714
  291. package/src/orchestrate/interface/utils/AutoBeJsonSchemaNamingConvention.ts +86 -86
  292. package/src/orchestrate/interface/utils/AutoBeJsonSchemaValidator.ts +725 -725
  293. package/src/orchestrate/interface/utils/fulfillJsonSchemaErrorMessages.ts +219 -219
  294. package/src/orchestrate/prisma/histories/transformPrismaAuthorizationHistory.ts +144 -144
  295. package/src/orchestrate/prisma/histories/transformPrismaAuthorizationReviewHistory.ts +104 -104
  296. package/src/orchestrate/prisma/histories/transformPrismaComponentReviewHistory.ts +97 -97
  297. package/src/orchestrate/prisma/histories/transformPrismaComponentsHistory.ts +136 -136
  298. package/src/orchestrate/prisma/histories/transformPrismaCorrectHistory.ts +41 -41
  299. package/src/orchestrate/prisma/histories/transformPrismaGroupHistory.ts +63 -63
  300. package/src/orchestrate/prisma/histories/transformPrismaGroupReviewHistory.ts +79 -79
  301. package/src/orchestrate/prisma/histories/transformPrismaSchemaHistory.ts +97 -97
  302. package/src/orchestrate/prisma/histories/transformPrismaSchemaReviewHistory.ts +126 -126
  303. package/src/orchestrate/prisma/index.ts +7 -7
  304. package/src/orchestrate/prisma/orchestratePrisma.ts +296 -296
  305. package/src/orchestrate/prisma/orchestratePrismaAuthorization.ts +173 -173
  306. package/src/orchestrate/prisma/orchestratePrismaAuthorizationReview.ts +183 -183
  307. package/src/orchestrate/prisma/orchestratePrismaComponent.ts +205 -205
  308. package/src/orchestrate/prisma/orchestratePrismaComponentReview.ts +205 -205
  309. package/src/orchestrate/prisma/orchestratePrismaCorrect.ts +265 -265
  310. package/src/orchestrate/prisma/orchestratePrismaGroup.ts +121 -121
  311. package/src/orchestrate/prisma/orchestratePrismaGroupReview.ts +141 -141
  312. package/src/orchestrate/prisma/orchestratePrismaSchema.ts +218 -218
  313. package/src/orchestrate/prisma/orchestratePrismaSchemaReview.ts +220 -220
  314. package/src/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationProgrammer.ts +86 -86
  315. package/src/orchestrate/prisma/programmers/AutoBeDatabaseAuthorizationReviewProgrammer.ts +103 -103
  316. package/src/orchestrate/prisma/programmers/AutoBeDatabaseComponentProgrammer.ts +80 -80
  317. package/src/orchestrate/prisma/programmers/AutoBeDatabaseComponentReviewProgrammer.ts +168 -168
  318. package/src/orchestrate/prisma/programmers/AutoBeDatabaseGroupProgrammer.ts +63 -63
  319. package/src/orchestrate/prisma/programmers/AutoBeDatabaseGroupReviewProgrammer.ts +210 -210
  320. package/src/orchestrate/prisma/programmers/AutoBeDatabaseModelProgrammer.ts +57 -57
  321. package/src/orchestrate/prisma/programmers/AutoBeDatabaseSchemaProgrammer.ts +92 -92
  322. package/src/orchestrate/prisma/structures/IAutoBeDatabaseAuthorizationApplication.ts +147 -147
  323. package/src/orchestrate/prisma/structures/IAutoBeDatabaseAuthorizationReviewApplication.ts +153 -153
  324. package/src/orchestrate/prisma/structures/IAutoBeDatabaseComponentApplication.ts +142 -142
  325. package/src/orchestrate/prisma/structures/IAutoBeDatabaseComponentReviewApplication.ts +151 -151
  326. package/src/orchestrate/prisma/structures/IAutoBeDatabaseCorrectApplication.ts +157 -157
  327. package/src/orchestrate/prisma/structures/IAutoBeDatabaseGroupApplication.ts +117 -117
  328. package/src/orchestrate/prisma/structures/IAutoBeDatabaseGroupReviewApplication.ts +154 -154
  329. package/src/orchestrate/prisma/structures/IAutoBeDatabaseSchemaApplication.ts +144 -144
  330. package/src/orchestrate/prisma/structures/IAutoBeDatabaseSchemaReviewApplication.ts +138 -138
  331. package/src/orchestrate/realize/histories/transformRealizeAuthorizationCorrectHistory.ts +89 -89
  332. package/src/orchestrate/realize/histories/transformRealizeAuthorizationWriteHistory.ts +45 -45
  333. package/src/orchestrate/realize/histories/transformRealizeCollectorCorrectHistory.ts +137 -137
  334. package/src/orchestrate/realize/histories/transformRealizeCollectorPlanHistory.ts +64 -64
  335. package/src/orchestrate/realize/histories/transformRealizeCollectorWriteHistory.ts +147 -147
  336. package/src/orchestrate/realize/histories/transformRealizeCorrectCastingHistory.ts +69 -69
  337. package/src/orchestrate/realize/histories/transformRealizeOperationCorrectHistory.ts +94 -94
  338. package/src/orchestrate/realize/histories/transformRealizeOperationWriteHistory.ts +127 -127
  339. package/src/orchestrate/realize/histories/transformRealizeTransformerCorrectHistory.ts +147 -147
  340. package/src/orchestrate/realize/histories/transformRealizeTransformerPlanHistory.ts +61 -61
  341. package/src/orchestrate/realize/histories/transformRealizeTransformerWriteHistory.ts +133 -133
  342. package/src/orchestrate/realize/histories/transformRealizeWriteMembershipHistory.ts +30 -30
  343. package/src/orchestrate/realize/index.ts +3 -3
  344. package/src/orchestrate/realize/internal/orchestrateRealizeCorrectCasting.ts +444 -444
  345. package/src/orchestrate/realize/internal/orchestrateRealizeCorrectOverall.ts +449 -449
  346. package/src/orchestrate/realize/orchestrateRealize.ts +143 -143
  347. package/src/orchestrate/realize/orchestrateRealizeAuthorizationCorrect.ts +207 -207
  348. package/src/orchestrate/realize/orchestrateRealizeAuthorizationWrite.ts +209 -209
  349. package/src/orchestrate/realize/orchestrateRealizeCollector.ts +41 -41
  350. package/src/orchestrate/realize/orchestrateRealizeCollectorCorrectCasting.ts +43 -43
  351. package/src/orchestrate/realize/orchestrateRealizeCollectorCorrectOverall.ts +157 -157
  352. package/src/orchestrate/realize/orchestrateRealizeCollectorPlan.ts +264 -264
  353. package/src/orchestrate/realize/orchestrateRealizeCollectorWrite.ts +225 -225
  354. package/src/orchestrate/realize/orchestrateRealizeOperation.ts +48 -48
  355. package/src/orchestrate/realize/orchestrateRealizeOperationCorrectCasting.ts +69 -69
  356. package/src/orchestrate/realize/orchestrateRealizeOperationCorrectOverall.ts +173 -173
  357. package/src/orchestrate/realize/orchestrateRealizeOperationWrite.ts +262 -262
  358. package/src/orchestrate/realize/orchestrateRealizeTransformer.ts +41 -41
  359. package/src/orchestrate/realize/orchestrateRealizeTransformerCorrectCasting.ts +38 -38
  360. package/src/orchestrate/realize/orchestrateRealizeTransformerCorrectOverall.ts +160 -160
  361. package/src/orchestrate/realize/orchestrateRealizeTransformerPlan.ts +254 -254
  362. package/src/orchestrate/realize/orchestrateRealizeTransformerWrite.ts +226 -226
  363. package/src/orchestrate/realize/programmers/AutoBeRealizeCollectorProgrammer.ts +424 -424
  364. package/src/orchestrate/realize/programmers/AutoBeRealizeOperationProgrammer.ts +409 -409
  365. package/src/orchestrate/realize/programmers/AutoBeRealizeTransformerProgrammer.ts +451 -451
  366. package/src/orchestrate/realize/programmers/compileRealizeFiles.ts +65 -65
  367. package/src/orchestrate/realize/structures/IAutoBeRealizeAuthorizationCorrectApplication.ts +119 -119
  368. package/src/orchestrate/realize/structures/IAutoBeRealizeAuthorizationWriteApplication.ts +167 -167
  369. package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorCorrectApplication.ts +191 -191
  370. package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorPlanApplication.ts +177 -177
  371. package/src/orchestrate/realize/structures/IAutoBeRealizeCollectorWriteApplication.ts +207 -207
  372. package/src/orchestrate/realize/structures/IAutoBeRealizeFunctionFailure.ts +11 -11
  373. package/src/orchestrate/realize/structures/IAutoBeRealizeOperationCorrectApplication.ts +134 -134
  374. package/src/orchestrate/realize/structures/IAutoBeRealizeOperationWriteApplication.ts +138 -138
  375. package/src/orchestrate/realize/structures/IAutoBeRealizeScenarioResult.ts +32 -32
  376. package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerCorrectApplication.ts +248 -248
  377. package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerPlanApplication.ts +150 -150
  378. package/src/orchestrate/realize/structures/IAutoBeRealizeTransformerWriteApplication.ts +278 -278
  379. package/src/orchestrate/realize/utils/AuthorizationFileSystem.ts +10 -10
  380. package/src/orchestrate/realize/utils/AutoBeRealizeAuthorizationFileSystem.ts +9 -9
  381. package/src/orchestrate/realize/utils/AutoBeRealizeAuthorizationReplaceImport.ts +62 -62
  382. package/src/orchestrate/realize/utils/InternalFileSystem.ts +12 -12
  383. package/src/orchestrate/realize/utils/ProviderFileSystem.ts +4 -4
  384. package/src/orchestrate/realize/utils/filterDiagnostics.ts +25 -25
  385. package/src/orchestrate/realize/utils/generateTS2339Hints.ts +54 -54
  386. package/src/orchestrate/realize/utils/getRealizeWriteImportStatements.ts +42 -42
  387. package/src/orchestrate/realize/utils/getRealizeWriteInputType.ts +88 -88
  388. package/src/orchestrate/realize/utils/printErrorHints.ts +54 -54
  389. package/src/orchestrate/test/compile/getTestArtifacts.ts +124 -124
  390. package/src/orchestrate/test/compile/getTestExternalDeclarations.ts +39 -39
  391. package/src/orchestrate/test/compile/getTestImportStatements.ts +27 -27
  392. package/src/orchestrate/test/experimental/orchestrateTestCorrect.ast +237 -237
  393. package/src/orchestrate/test/experimental/orchestrateTestWrite.ast +322 -322
  394. package/src/orchestrate/test/experimental/transformTestCorrectHistories.ast +52 -52
  395. package/src/orchestrate/test/histories/transformTestAuthorizeWriteHistory.ts +80 -80
  396. package/src/orchestrate/test/histories/transformTestCorrectOverallHistory.ts +116 -116
  397. package/src/orchestrate/test/histories/transformTestGenerationWriteHistory.ts +118 -118
  398. package/src/orchestrate/test/histories/transformTestOperationWriteHistory.ts +287 -287
  399. package/src/orchestrate/test/histories/transformTestPrepareWriteHistory.ts +95 -95
  400. package/src/orchestrate/test/histories/transformTestScenarioHistory.ts +120 -120
  401. package/src/orchestrate/test/histories/transformTestScenarioReviewHistory.ts +99 -99
  402. package/src/orchestrate/test/histories/transformTestValidateEvent.ts +11 -11
  403. package/src/orchestrate/test/index.ts +5 -5
  404. package/src/orchestrate/test/internal/orchestrateTestCorrectCasting.ts +96 -96
  405. package/src/orchestrate/test/internal/orchestrateTestCorrectOverall.ts +219 -219
  406. package/src/orchestrate/test/internal/orchestrateTestCorrectRequest.ts +206 -206
  407. package/src/orchestrate/test/orchestrateTest.ts +161 -161
  408. package/src/orchestrate/test/orchestrateTestAuthorize.ts +125 -125
  409. package/src/orchestrate/test/orchestrateTestAuthorizeWrite.ts +263 -263
  410. package/src/orchestrate/test/orchestrateTestGenerate.ts +121 -121
  411. package/src/orchestrate/test/orchestrateTestGenerateWrite.ts +211 -211
  412. package/src/orchestrate/test/orchestrateTestOperation.ts +133 -133
  413. package/src/orchestrate/test/orchestrateTestOperationWrite.ts +221 -221
  414. package/src/orchestrate/test/orchestrateTestPrepare.ts +117 -117
  415. package/src/orchestrate/test/orchestrateTestPrepareWrite.ts +264 -264
  416. package/src/orchestrate/test/orchestrateTestScenario.ts +290 -290
  417. package/src/orchestrate/test/orchestrateTestScenarioReview.ts +262 -262
  418. package/src/orchestrate/test/programmers/AutoBeTestAuthorizeProgrammer.ts +149 -149
  419. package/src/orchestrate/test/programmers/AutoBeTestFunctionProgrammer.ts +90 -90
  420. package/src/orchestrate/test/programmers/AutoBeTestGenerateProgrammer.ts +170 -170
  421. package/src/orchestrate/test/programmers/AutoBeTestOperationProgrammer.ts +168 -168
  422. package/src/orchestrate/test/programmers/AutoBeTestPrepareProgrammer.ts +261 -261
  423. package/src/orchestrate/test/programmers/AutoBeTestScenarioProgrammer.ts +316 -316
  424. package/src/orchestrate/test/structures/IAutoBeTestArtifacts.ts +8 -8
  425. package/src/orchestrate/test/structures/IAutoBeTestAuthorizationWriteApplication.ts +113 -113
  426. package/src/orchestrate/test/structures/IAutoBeTestAuthorizeWriteResult.ts +10 -10
  427. package/src/orchestrate/test/structures/IAutoBeTestCorrectOverallApplication.ts +134 -134
  428. package/src/orchestrate/test/structures/IAutoBeTestCorrectRequestApplication.ts +152 -152
  429. package/src/orchestrate/test/structures/IAutoBeTestFunction.ts +10 -10
  430. package/src/orchestrate/test/structures/IAutoBeTestFunctionFailure.ts +10 -10
  431. package/src/orchestrate/test/structures/IAutoBeTestGenerateProcedure.ts +15 -15
  432. package/src/orchestrate/test/structures/IAutoBeTestGenerationWriteApplication.ts +145 -145
  433. package/src/orchestrate/test/structures/IAutoBeTestOperationProcedure.ts +17 -17
  434. package/src/orchestrate/test/structures/IAutoBeTestOperationWriteApplication.ts +162 -162
  435. package/src/orchestrate/test/structures/IAutoBeTestPrepareCorrectOverallApplication.ts +194 -194
  436. package/src/orchestrate/test/structures/IAutoBeTestPrepareProcedure.ts +8 -8
  437. package/src/orchestrate/test/structures/IAutoBeTestPrepareWriteApplication.ts +147 -147
  438. package/src/orchestrate/test/structures/IAutoBeTestProcedure.ts +10 -10
  439. package/src/orchestrate/test/structures/IAutoBeTestScenarioApplication.ts +105 -105
  440. package/src/orchestrate/test/structures/IAutoBeTestScenarioArtifacts.ts +8 -8
  441. package/src/orchestrate/test/structures/IAutoBeTestScenarioAuthorizationActor.ts +7 -7
  442. package/src/orchestrate/test/structures/IAutoBeTestScenarioReviewApplication.ts +133 -133
  443. package/src/orchestrate/test/utils/getPrerequisites.ts +51 -51
  444. package/src/orchestrate/test/utils/getReferenceIds.ts +26 -26
  445. package/src/orchestrate/test/utils/insertScriptToTestResult.ts +14 -14
  446. package/src/structures/IAutoBeConfig.ts +110 -110
  447. package/src/structures/IAutoBeOrchestrateHistory.ts +44 -44
  448. package/src/structures/IAutoBeProps.ts +102 -102
  449. package/src/structures/IAutoBeVendor.ts +113 -113
  450. package/src/utils/AutoBePreliminaryExhaustedError.ts +30 -30
  451. package/src/utils/AutoBeTimeoutError.ts +35 -35
  452. package/src/utils/EmbeddingProvider.ts +4 -4
  453. package/src/utils/LocalEmbeddingProvider.ts +145 -145
  454. package/src/utils/RAGRetrieval.ts +411 -411
  455. package/src/utils/TimedConversation.ts +125 -125
  456. package/src/utils/backoffRetry.ts +169 -169
  457. package/src/utils/divideArray.ts +35 -35
  458. package/src/utils/emplaceMap.ts +31 -31
  459. package/src/utils/executeCachedBatch.ts +67 -67
  460. package/src/utils/forceRetry.ts +15 -15
  461. package/src/utils/getEmbedder.ts +16 -16
  462. package/src/utils/parseTextFunctionCall.ts +437 -437
  463. package/src/utils/predicateStateMessage.ts +131 -131
  464. package/src/utils/validateEmptyCode.ts +73 -73
  465. package/src/utils/validateEnglishOnly.ts +224 -224
  466. package/README.md +0 -261
@@ -1,1693 +1,1693 @@
1
- import { AgenticaValidationError } from "@agentica/core";
2
- import {
3
- AutoBeAnalyze,
4
- AutoBeAnalyzeHistory,
5
- AutoBeAnalyzeScenarioEvent,
6
- AutoBeAnalyzeScenarioReviewEvent,
7
- AutoBeAnalyzeSectionReviewEvent,
8
- AutoBeAnalyzeSectionReviewFileResult,
9
- AutoBeAnalyzeSectionReviewIssue,
10
- AutoBeAnalyzeSectionReviewRejectedModuleUnit,
11
- AutoBeAnalyzeWriteModuleEvent,
12
- AutoBeAnalyzeWriteSectionEvent,
13
- AutoBeAnalyzeWriteUnitEvent,
14
- AutoBeAssistantMessageHistory,
15
- AutoBeProgressEventBase,
16
- } from "@autobe/interface";
17
- import { v7 } from "uuid";
18
-
19
- import { AutoBeConfigConstant } from "../../constants/AutoBeConfigConstant";
20
- import { AutoBeContext } from "../../context/AutoBeContext";
21
- import { AutoBePreliminaryExhaustedError } from "../../utils/AutoBePreliminaryExhaustedError";
22
- import { AutoBeTimeoutError } from "../../utils/AutoBeTimeoutError";
23
- import { executeCachedBatch } from "../../utils/executeCachedBatch";
24
- import { fillTocDeterministic } from "./fillTocDeterministic";
25
- import { orchestrateAnalyzeScenario } from "./orchestrateAnalyzeScenario";
26
- import { orchestrateAnalyzeScenarioReview } from "./orchestrateAnalyzeScenarioReview";
27
- import { orchestrateAnalyzeSectionCrossFileReview } from "./orchestrateAnalyzeSectionCrossFileReview";
28
- import { orchestrateAnalyzeSectionReview } from "./orchestrateAnalyzeSectionReview";
29
- import { orchestrateAnalyzeWriteSection } from "./orchestrateAnalyzeWriteSection";
30
- import { orchestrateAnalyzeWriteSectionPatch } from "./orchestrateAnalyzeWriteSectionPatch";
31
- import { orchestrateAnalyzeWriteUnit } from "./orchestrateAnalyzeWriteUnit";
32
- import {
33
- assembleContent,
34
- assembleModule,
35
- } from "./programmers/AutoBeAnalyzeProgrammer";
36
- import {
37
- FixedAnalyzeTemplateFeature,
38
- FixedAnalyzeTemplateUnitTemplate,
39
- buildFixedAnalyzeExpandedTemplate,
40
- expandFixedAnalyzeTemplateUnits,
41
- } from "./structures/FixedAnalyzeTemplate";
42
- import {
43
- buildFileAttributeDuplicateMap,
44
- buildFileConflictMap,
45
- buildFileEnumConflictMap,
46
- buildFilePermissionConflictMap,
47
- buildFileStateFieldConflictMap,
48
- detectAttributeDuplicates,
49
- detectConstraintConflicts,
50
- detectEnumConflicts,
51
- detectPermissionConflicts,
52
- detectStateFieldConflicts,
53
- } from "./utils/buildConstraintConsistencyReport";
54
- import {
55
- buildFileErrorCodeConflictMap,
56
- detectErrorCodeConflicts,
57
- } from "./utils/buildErrorCodeRegistry";
58
- import { detectOversizedToc } from "./utils/buildHardValidators";
59
- import {
60
- buildFileProseConflictMap,
61
- detectProseConstraintConflicts,
62
- } from "./utils/detectProseConstraintConflicts";
63
- import { validateScenarioBasics } from "./utils/validateScenarioBasics";
64
-
65
- /**
66
- * Per-file state tracking across all three stages (Module → Unit → Section).
67
- *
68
- * Maintains each file's intermediate results and cross-file review feedback
69
- * throughout the stage-synchronized pipeline.
70
- */
71
- interface IFileState {
72
- file: AutoBeAnalyze.IFileScenario;
73
- moduleResult: AutoBeAnalyzeWriteModuleEvent | null;
74
- unitResults: AutoBeAnalyzeWriteUnitEvent[] | null;
75
- sectionResults: AutoBeAnalyzeWriteSectionEvent[][] | null;
76
- sectionFeedback?: string;
77
- // Section-stage partial regeneration tracking
78
- rejectedModuleUnits?: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null;
79
- sectionRetryCount?: number;
80
- sectionReviewCount?: number;
81
- sectionStagnationCount?: number;
82
- lastSectionContentSignature?: string;
83
- lastSectionRejectionSignature?: string;
84
- }
85
-
86
- const ANALYZE_SCENARIO_MAX_RETRY = 2;
87
- const ANALYZE_SECTION_FILE_MAX_RETRY = 5;
88
- const ANALYZE_SECTION_FILE_MAX_REVIEW = 2;
89
- const ANALYZE_SECTION_STAGNATION_MAX = 4;
90
- const ANALYZE_DEBUG_LOG = process.env.AUTOBE_DEBUG_ANALYZE === "1";
91
-
92
- const analyzeDebug = (message: string): void => {
93
- if (!ANALYZE_DEBUG_LOG) return;
94
- console.log(`[analyze-debug] ${new Date().toISOString()} ${message}`);
95
- };
96
-
97
- export const orchestrateAnalyze = async (
98
- ctx: AutoBeContext,
99
- ): Promise<AutoBeAssistantMessageHistory | AutoBeAnalyzeHistory> => {
100
- // Initialize analysis state
101
- const step: number = (ctx.state().analyze?.step ?? -1) + 1;
102
- const startTime: Date = new Date();
103
-
104
- ctx.dispatch({
105
- type: "analyzeStart",
106
- id: v7(),
107
- step,
108
- created_at: startTime.toISOString(),
109
- });
110
-
111
- // Generate analysis scenario with pre-check + LLM review + retry loop
112
- let scenario!: AutoBeAnalyzeScenarioEvent;
113
- let scenarioFeedback: string | undefined;
114
-
115
- for (let attempt = 0; attempt <= ANALYZE_SCENARIO_MAX_RETRY; attempt++) {
116
- const rawScenario = await orchestrateAnalyzeScenario(ctx, {
117
- feedback: scenarioFeedback,
118
- });
119
- if (rawScenario.type === "assistantMessage")
120
- return ctx.assistantMessage(rawScenario);
121
-
122
- // 1) Programmatic pre-check
123
- const preCheck = validateScenarioBasics({
124
- prefix: rawScenario.prefix,
125
- actors: rawScenario.actors,
126
- entities: rawScenario.entities,
127
- });
128
- if (!preCheck.valid && attempt < ANALYZE_SCENARIO_MAX_RETRY) {
129
- analyzeDebug(
130
- `Scenario pre-check failed (attempt ${attempt}): ${preCheck.errors.join("; ")}`,
131
- );
132
- scenarioFeedback = `Programmatic validation failed:\n${preCheck.errors.join("\n")}`;
133
- continue;
134
- }
135
-
136
- // 2) LLM review
137
- let review: AutoBeAnalyzeScenarioReviewEvent;
138
- try {
139
- review = await orchestrateAnalyzeScenarioReview(ctx, {
140
- scenario: rawScenario,
141
- retry: attempt,
142
- });
143
- } catch (e) {
144
- if (
145
- e instanceof AgenticaValidationError ||
146
- e instanceof AutoBePreliminaryExhaustedError ||
147
- e instanceof AutoBeTimeoutError
148
- ) {
149
- analyzeDebug(
150
- `scenario review force-pass (attempt ${attempt}) error=${(e as Error).constructor.name}`,
151
- );
152
- review = {
153
- type: "analyzeScenarioReview",
154
- id: v7(),
155
- approved: true,
156
- feedback:
157
- "Review could not be completed; proceeding with current scenario.",
158
- issues: [],
159
- tokenUsage: {
160
- total: 0,
161
- input: { total: 0, cached: 0 },
162
- output: {
163
- total: 0,
164
- reasoning: 0,
165
- accepted_prediction: 0,
166
- rejected_prediction: 0,
167
- },
168
- },
169
- metric: {
170
- attempt: 0,
171
- success: 0,
172
- consent: 0,
173
- validationFailure: 0,
174
- invalidJson: 0,
175
- },
176
- step: (ctx.state().analyze?.step ?? -1) + 1,
177
- retry: attempt,
178
- created_at: new Date().toISOString(),
179
- };
180
- } else {
181
- throw e;
182
- }
183
- }
184
-
185
- if (review.approved || attempt >= ANALYZE_SCENARIO_MAX_RETRY) {
186
- analyzeDebug(
187
- review.approved
188
- ? `Scenario approved (attempt ${attempt})`
189
- : `Scenario max retry reached (attempt ${attempt}), proceeding`,
190
- );
191
- scenario = rawScenario;
192
- ctx.dispatch(scenario);
193
- break;
194
- }
195
-
196
- analyzeDebug(`Scenario rejected (attempt ${attempt}): ${review.feedback}`);
197
- scenarioFeedback = review.feedback;
198
- }
199
-
200
- // Initialize per-file state
201
- const fileStates: IFileState[] = scenario.files.map((file) => ({
202
- file,
203
- moduleResult: null,
204
- unitResults: null,
205
- sectionResults: null,
206
- }));
207
-
208
- // Progress tracking for each stage
209
- const moduleWriteProgress: AutoBeProgressEventBase = {
210
- total: scenario.files.length,
211
- completed: 0,
212
- };
213
- const unitWriteProgress: AutoBeProgressEventBase = {
214
- total: 0,
215
- completed: 0,
216
- };
217
- const sectionWriteProgress: AutoBeProgressEventBase = {
218
- total: 0,
219
- completed: 0,
220
- };
221
- const perFileSectionReviewProgress: AutoBeProgressEventBase = {
222
- total: 0,
223
- completed: 0,
224
- };
225
- const crossFileSectionReviewProgress: AutoBeProgressEventBase = {
226
- total: 1,
227
- completed: 0,
228
- };
229
-
230
- // === STAGE 1: MODULE (deterministic — no LLM) ===
231
- processStageModuleDeterministic(ctx, {
232
- scenario,
233
- fileStates,
234
- moduleWriteProgress,
235
- });
236
-
237
- // === STAGE 2: UNIT (fixed units deterministic, dynamic units LLM) ===
238
- await processStageUnit(ctx, {
239
- scenario,
240
- fileStates,
241
- unitWriteProgress,
242
- });
243
-
244
- // === STAGE 3: SECTION (01-05 only, TOC excluded) ===
245
- await processStageSection(ctx, {
246
- scenario,
247
- fileStates,
248
- sectionWriteProgress,
249
- perFileSectionReviewProgress,
250
- crossFileSectionReviewProgress,
251
- });
252
-
253
- // === TOC FILL (deterministic — no LLM) ===
254
- const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
255
- (scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
256
- );
257
- const tocIndex = fileStates.findIndex((s) => s.file.filename === "00-toc.md");
258
- let tocContent: string | null = null;
259
- if (tocIndex >= 0) {
260
- tocContent = fillTocDeterministic(ctx, {
261
- scenario,
262
- tocFileState: fileStates[tocIndex]!,
263
- otherFileStates: fileStates.filter((_, i) => i !== tocIndex),
264
- expandedTemplate,
265
- });
266
- }
267
-
268
- // === ASSEMBLE ===
269
- const files: AutoBeAnalyze.IFile[] = [];
270
- for (let fileIndex = 0; fileIndex < fileStates.length; fileIndex++) {
271
- const state = fileStates[fileIndex]!;
272
- // TOC uses flat content directly (no module/unit hierarchy)
273
- const content =
274
- fileIndex === tocIndex
275
- ? tocContent!
276
- : assembleContent(
277
- state.moduleResult!,
278
- state.unitResults!,
279
- state.sectionResults!,
280
- );
281
- const module = assembleModule(
282
- state.moduleResult!,
283
- state.unitResults!,
284
- state.sectionResults!,
285
- );
286
-
287
- files.push({
288
- ...state.file,
289
- title: state.moduleResult!.title,
290
- summary: state.moduleResult!.summary,
291
- content,
292
- module,
293
- });
294
- }
295
-
296
- // Complete the analysis
297
- return ctx.dispatch({
298
- type: "analyzeComplete",
299
- id: v7(),
300
- actors: scenario.actors,
301
- prefix: scenario.prefix,
302
- files,
303
- aggregates: ctx.getCurrentAggregates("analyze"),
304
- step,
305
- elapsed: new Date().getTime() - startTime.getTime(),
306
- created_at: new Date().toISOString(),
307
- }) satisfies AutoBeAnalyzeHistory;
308
- };
309
-
310
- // MODULE (deterministic — no LLM calls)
311
-
312
- /**
313
- * Generate module structure deterministically from FixedAnalyzeTemplate.
314
- *
315
- * No LLM calls needed — module titles, purposes, and structure are all derived
316
- * from the fixed 6-file SRS template.
317
- */
318
- function processStageModuleDeterministic(
319
- ctx: AutoBeContext,
320
- props: {
321
- scenario: AutoBeAnalyzeScenarioEvent;
322
- fileStates: IFileState[];
323
- moduleWriteProgress: AutoBeProgressEventBase;
324
- },
325
- ): void {
326
- const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
327
- (props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
328
- );
329
- for (const [i, state] of props.fileStates.entries()) {
330
- const template = expandedTemplate[i]!;
331
- const moduleEvent: AutoBeAnalyzeWriteModuleEvent = {
332
- type: "analyzeWriteModule",
333
- id: v7(),
334
- title: `${props.scenario.prefix} — ${template.description}`,
335
- summary: template.description,
336
- moduleSections: template.modules.map((m) => ({
337
- title: m.title,
338
- purpose: m.purpose,
339
- content: m.purpose,
340
- })),
341
- step: (ctx.state().analyze?.step ?? -1) + 1,
342
- retry: 0,
343
- total: props.fileStates.length,
344
- completed: i + 1,
345
- tokenUsage: {
346
- total: 0,
347
- input: { total: 0, cached: 0 },
348
- output: {
349
- total: 0,
350
- reasoning: 0,
351
- accepted_prediction: 0,
352
- rejected_prediction: 0,
353
- },
354
- },
355
- metric: {
356
- attempt: 0,
357
- success: 0,
358
- consent: 0,
359
- validationFailure: 0,
360
- invalidJson: 0,
361
- },
362
- acquisition: { previousAnalysisSections: [] },
363
- created_at: new Date().toISOString(),
364
- };
365
- state.moduleResult = moduleEvent;
366
- ctx.dispatch(moduleEvent);
367
- props.moduleWriteProgress.completed++;
368
- }
369
- }
370
-
371
- // UNIT
372
-
373
- /**
374
- * Process the Unit stage for all files.
375
- *
376
- * Fixed-strategy modules get deterministic unit generation (no LLM).
377
- * Dynamic-strategy modules (perEntity/perActor/perEntityGroup) use LLM. No
378
- * cross-file unit review — Hard Validators at section stage handle
379
- * consistency.
380
- */
381
- async function processStageUnit(
382
- ctx: AutoBeContext,
383
- props: {
384
- scenario: AutoBeAnalyzeScenarioEvent;
385
- fileStates: IFileState[];
386
- unitWriteProgress: AutoBeProgressEventBase;
387
- },
388
- ): Promise<void> {
389
- const promptCacheKey: string = v7();
390
- const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
391
- (props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
392
- );
393
-
394
- // Count total units needed for progress tracking
395
- for (const [fileIndex, state] of props.fileStates.entries()) {
396
- const template = expandedTemplate[fileIndex]!;
397
- props.unitWriteProgress.total += template.modules.length;
398
- void state; // used below
399
- }
400
-
401
- await executeCachedBatch(
402
- ctx,
403
- props.fileStates.map((state, fileIndex) => async (cacheKey) => {
404
- // TOC is filled deterministically after all other files complete
405
- if (state.file.filename === "00-toc.md") return [];
406
-
407
- const moduleResult: AutoBeAnalyzeWriteModuleEvent = state.moduleResult!;
408
- const template = expandedTemplate[fileIndex]!;
409
- analyzeDebug(
410
- `unit file-start fileIndex=${fileIndex} file="${state.file.filename}"`,
411
- );
412
-
413
- const unitResults: AutoBeAnalyzeWriteUnitEvent[] = [];
414
- for (
415
- let moduleIndex: number = 0;
416
- moduleIndex < moduleResult.moduleSections.length;
417
- moduleIndex++
418
- ) {
419
- const moduleTemplate = template.modules[moduleIndex]!;
420
- const strategy = moduleTemplate.unitStrategy;
421
-
422
- if (strategy.type === "fixed") {
423
- // Deterministic unit generation — no LLM
424
- const unitEvent = buildDeterministicUnitEvent(ctx, {
425
- moduleIndex,
426
- units: strategy.units,
427
- progress: props.unitWriteProgress,
428
- });
429
- ctx.dispatch(unitEvent);
430
- unitResults.push(unitEvent);
431
- } else {
432
- // Dynamic units — expand from template, then LLM writes content+keywords
433
- const unitStart: number = Date.now();
434
- analyzeDebug(
435
- `unit module-start fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} strategy=${strategy.type}`,
436
- );
437
- try {
438
- const unitEvent: AutoBeAnalyzeWriteUnitEvent =
439
- await orchestrateAnalyzeWriteUnit(ctx, {
440
- scenario: props.scenario,
441
- file: state.file,
442
- moduleEvent: moduleResult,
443
- moduleIndex,
444
- progress: props.unitWriteProgress,
445
- promptCacheKey: cacheKey,
446
- retry: 0,
447
- });
448
- analyzeDebug(
449
- `unit module-done fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitCount=${unitEvent.unitSections.length} elapsedMs=${Date.now() - unitStart}`,
450
- );
451
- unitResults.push(unitEvent);
452
- } catch (e) {
453
- if (
454
- e instanceof AgenticaValidationError ||
455
- e instanceof AutoBePreliminaryExhaustedError ||
456
- e instanceof AutoBeTimeoutError
457
- ) {
458
- analyzeDebug(
459
- `unit module-skipped fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} elapsedMs=${Date.now() - unitStart} — using fallback`,
460
- );
461
- const expandedUnits = expandFixedAnalyzeTemplateUnits(
462
- moduleTemplate,
463
- props.scenario.entities,
464
- props.scenario.actors,
465
- );
466
- const fallbackEvent = buildDeterministicUnitEvent(ctx, {
467
- moduleIndex,
468
- units: expandedUnits,
469
- progress: props.unitWriteProgress,
470
- });
471
- ctx.dispatch(fallbackEvent);
472
- unitResults.push(fallbackEvent);
473
- } else {
474
- throw e;
475
- }
476
- }
477
- }
478
- }
479
- state.unitResults = unitResults;
480
- analyzeDebug(
481
- `unit file-done fileIndex=${fileIndex} file="${state.file.filename}"`,
482
- );
483
- return unitResults;
484
- }),
485
- promptCacheKey,
486
- );
487
- }
488
-
489
- /** Build a deterministic AutoBeAnalyzeWriteUnitEvent for fixed-strategy modules. */
490
- function buildDeterministicUnitEvent(
491
- ctx: AutoBeContext,
492
- props: {
493
- moduleIndex: number;
494
- units: FixedAnalyzeTemplateUnitTemplate[];
495
- progress: AutoBeProgressEventBase;
496
- },
497
- ): AutoBeAnalyzeWriteUnitEvent {
498
- props.progress.completed++;
499
- return {
500
- type: "analyzeWriteUnit",
501
- id: v7(),
502
- moduleIndex: props.moduleIndex,
503
- unitSections: props.units.map((u) => ({
504
- title: u.titlePattern,
505
- purpose: u.purposePattern,
506
- content: u.purposePattern,
507
- keywords: [...u.keywords],
508
- })),
509
- step: (ctx.state().analyze?.step ?? -1) + 1,
510
- retry: 0,
511
- total: props.progress.total,
512
- completed: props.progress.completed,
513
- tokenUsage: {
514
- total: 0,
515
- input: { total: 0, cached: 0 },
516
- output: {
517
- total: 0,
518
- reasoning: 0,
519
- accepted_prediction: 0,
520
- rejected_prediction: 0,
521
- },
522
- },
523
- metric: {
524
- attempt: 0,
525
- success: 0,
526
- consent: 0,
527
- validationFailure: 0,
528
- invalidJson: 0,
529
- },
530
- acquisition: { previousAnalysisSections: [] },
531
- created_at: new Date().toISOString(),
532
- };
533
- }
534
-
535
- // SECTION
536
-
537
- /**
538
- * Process the Section stage for all files with 2-pass review.
539
- *
540
- * Flow:
541
- *
542
- * 1. Write sections for pending files in parallel
543
- * 2. Pass 1: Per-file detailed review (parallel) — validates EARS format, value
544
- * consistency, bridge blocks, intra-file deduplication
545
- * 3. Pass 2: Cross-file lightweight review (single call) — validates terminology
546
- * alignment, value consistency across files, naming conventions
547
- * 4. Merge results from both passes — reject if either pass rejects
548
- * 5. Retry only rejected files (max 3 attempts)
549
- */
550
- async function processStageSection(
551
- ctx: AutoBeContext,
552
- props: {
553
- scenario: AutoBeAnalyzeScenarioEvent;
554
- fileStates: IFileState[];
555
- sectionWriteProgress: AutoBeProgressEventBase;
556
- perFileSectionReviewProgress: AutoBeProgressEventBase;
557
- crossFileSectionReviewProgress: AutoBeProgressEventBase;
558
- },
559
- ): Promise<void> {
560
- // Exclude TOC (00-toc.md) — it is filled deterministically after all files
561
- const pendingIndices: Set<number> = new Set(
562
- props.fileStates
563
- .map((s, i) => (s.file.filename === "00-toc.md" ? -1 : i))
564
- .filter((i) => i >= 0),
565
- );
566
- let crossFileReviewCount: number = 0;
567
-
568
- for (
569
- let attempt: number = 0;
570
- attempt < AutoBeConfigConstant.ANALYZE_RETRY && pendingIndices.size > 0;
571
- attempt++
572
- ) {
573
- // Dynamically increase progress for retries (module-level granularity)
574
- const pendingModuleCount = [...pendingIndices].reduce(
575
- (sum, fi) => sum + (props.fileStates[fi]?.unitResults?.length ?? 1),
576
- 0,
577
- );
578
- props.perFileSectionReviewProgress.total += pendingModuleCount;
579
- if (attempt > 0) {
580
- props.crossFileSectionReviewProgress.total++;
581
- }
582
-
583
- // Write sections for pending files in parallel
584
- const pendingArray: number[] = [...pendingIndices];
585
- const sectionFileBatches: number[][] = chunkSectionFileIndices(
586
- pendingArray,
587
- computeSectionBatchSize({
588
- attempt,
589
- pendingCount: pendingArray.length,
590
- }),
591
- );
592
- const promptCacheKey: string = v7();
593
-
594
- // Build scenario entity name list for invention validation (P0-B)
595
- const scenarioEntityNames = props.scenario.entities.map((e) => e.name);
596
-
597
- // Collect per-file review results (populated inside write+review batch)
598
- const perFileReviewResults: Map<number, AutoBeAnalyzeSectionReviewEvent> =
599
- new Map();
600
-
601
- for (const sectionBatch of sectionFileBatches)
602
- await executeCachedBatch(
603
- ctx,
604
- sectionBatch.map((fileIndex) => async (cacheKey) => {
605
- const state: IFileState = props.fileStates[fileIndex]!;
606
- const moduleResult: AutoBeAnalyzeWriteModuleEvent =
607
- state.moduleResult!;
608
- const unitResults: AutoBeAnalyzeWriteUnitEvent[] = state.unitResults!;
609
- analyzeDebug(
610
- `section file-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" batchSize=${sectionBatch.length}`,
611
- );
612
-
613
- // Build rejected module/unit lookup for selective regeneration
614
- const rejectedSet: Set<string> | null = buildRejectedSet(
615
- state.rejectedModuleUnits,
616
- );
617
- const feedbackMap: Map<string, ISectionAwareFeedback> =
618
- buildFeedbackMap(state.rejectedModuleUnits);
619
-
620
- // Increase write progress only for sections that will be regenerated
621
- for (let mi: number = 0; mi < unitResults.length; mi++) {
622
- const unitEvent: AutoBeAnalyzeWriteUnitEvent = unitResults[mi]!;
623
- for (let ui: number = 0; ui < unitEvent.unitSections.length; ui++) {
624
- if (isSectionRejected(rejectedSet, mi, ui)) {
625
- props.sectionWriteProgress.total++;
626
- }
627
- }
628
- }
629
-
630
- // Write sections, skipping approved ones on retry
631
- const sectionResults: AutoBeAnalyzeWriteSectionEvent[][] = [];
632
- for (
633
- let moduleIndex: number = 0;
634
- moduleIndex < unitResults.length;
635
- moduleIndex++
636
- ) {
637
- const unitEvent: AutoBeAnalyzeWriteUnitEvent =
638
- unitResults[moduleIndex]!;
639
- const sectionsForModule: AutoBeAnalyzeWriteSectionEvent[] = [];
640
-
641
- for (
642
- let unitIndex: number = 0;
643
- unitIndex < unitEvent.unitSections.length;
644
- unitIndex++
645
- ) {
646
- if (isSectionRejected(rejectedSet, moduleIndex, unitIndex)) {
647
- const sectionStart: number = Date.now();
648
- // Regenerate this section with targeted feedback
649
- const targetedInfo: ISectionAwareFeedback | undefined =
650
- feedbackMap.get(`${moduleIndex}:${unitIndex}`);
651
- const targetedFeedback: string | undefined =
652
- targetedInfo?.feedback ?? state.sectionFeedback;
653
- const targetedSectionIndices: number[] | null =
654
- targetedInfo?.sectionIndices ?? null;
655
- analyzeDebug(
656
- `section unit-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} targetSections=${targetedSectionIndices ? `[${targetedSectionIndices.join(",")}]` : "all"}`,
657
- );
658
- const previousSection:
659
- | AutoBeAnalyzeWriteSectionEvent
660
- | undefined =
661
- state.sectionResults?.[moduleIndex]?.[unitIndex];
662
- let sectionEvent: AutoBeAnalyzeWriteSectionEvent;
663
- try {
664
- sectionEvent =
665
- previousSection && targetedFeedback?.trim()
666
- ? await orchestrateAnalyzeWriteSectionPatch(ctx, {
667
- scenario: props.scenario,
668
- file: state.file,
669
- moduleEvent: moduleResult,
670
- unitEvent,
671
- moduleIndex,
672
- unitIndex,
673
- previousSectionEvent: previousSection,
674
- feedback: targetedFeedback,
675
- progress: props.sectionWriteProgress,
676
- promptCacheKey: cacheKey,
677
- retry: attempt,
678
- scenarioEntityNames,
679
- sectionIndices: targetedSectionIndices,
680
- })
681
- : await orchestrateAnalyzeWriteSection(ctx, {
682
- scenario: props.scenario,
683
- file: state.file,
684
- moduleEvent: moduleResult,
685
- unitEvent,
686
- allUnitEvents: unitResults,
687
- moduleIndex,
688
- unitIndex,
689
- progress: props.sectionWriteProgress,
690
- promptCacheKey: cacheKey,
691
- feedback: targetedFeedback,
692
- retry: attempt,
693
- scenarioEntityNames,
694
- });
695
- } catch (e) {
696
- if (
697
- e instanceof AgenticaValidationError ||
698
- e instanceof AutoBePreliminaryExhaustedError ||
699
- e instanceof AutoBeTimeoutError
700
- ) {
701
- analyzeDebug(
702
- `section unit-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} error=${(e as Error).constructor.name} — ${previousSection ? "reusing previous" : "using placeholder"}`,
703
- );
704
- if (previousSection) {
705
- sectionEvent = previousSection;
706
- } else {
707
- sectionEvent = {
708
- type: "analyzeWriteSection",
709
- id: v7(),
710
- moduleIndex,
711
- unitIndex,
712
- sectionSections: [],
713
- acquisition: { previousAnalysisSections: [] },
714
- tokenUsage: {
715
- total: 0,
716
- input: { total: 0, cached: 0 },
717
- output: {
718
- total: 0,
719
- reasoning: 0,
720
- accepted_prediction: 0,
721
- rejected_prediction: 0,
722
- },
723
- },
724
- metric: {
725
- attempt: 0,
726
- success: 0,
727
- consent: 0,
728
- validationFailure: 0,
729
- invalidJson: 0,
730
- },
731
- step: (ctx.state().analyze?.step ?? -1) + 1,
732
- total: props.sectionWriteProgress.total,
733
- completed: ++props.sectionWriteProgress.completed,
734
- retry: attempt,
735
- created_at: new Date().toISOString(),
736
- };
737
- ctx.dispatch(sectionEvent);
738
- }
739
- } else {
740
- throw e;
741
- }
742
- }
743
- analyzeDebug(
744
- `section unit-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} sectionCount=${sectionEvent.sectionSections.length} elapsedMs=${Date.now() - sectionStart}`,
745
- );
746
- sectionsForModule.push(sectionEvent);
747
- } else {
748
- // Keep existing approved section
749
- sectionsForModule.push(
750
- state.sectionResults![moduleIndex]![unitIndex]!,
751
- );
752
- }
753
- }
754
- sectionResults.push(sectionsForModule);
755
- }
756
- state.sectionResults = sectionResults;
757
-
758
- analyzeDebug(
759
- `section file-write-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}"`,
760
- );
761
-
762
- // Per-module review immediately after write (removes barrier)
763
- const reviewStart: number = Date.now();
764
- analyzeDebug(
765
- `section per-module-review-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${state.unitResults!.length}`,
766
- );
767
- const moduleReviews: AutoBeAnalyzeSectionReviewEvent[] = [];
768
- for (
769
- let moduleIndex = 0;
770
- moduleIndex < state.unitResults!.length;
771
- moduleIndex++
772
- ) {
773
- try {
774
- const moduleReviewEvent = await orchestrateAnalyzeSectionReview(
775
- ctx,
776
- {
777
- scenario: props.scenario,
778
- fileIndex,
779
- file: state.file,
780
- moduleEvent: state.moduleResult!,
781
- moduleIndex,
782
- unitEvent: state.unitResults![moduleIndex]!,
783
- moduleSectionEvents: state.sectionResults![moduleIndex]!,
784
- siblingModuleSummaries: buildSiblingModuleSummaries(
785
- state.moduleResult!,
786
- state.sectionResults!,
787
- moduleIndex,
788
- ),
789
- feedback: state.sectionFeedback,
790
- progress: props.perFileSectionReviewProgress,
791
- promptCacheKey: cacheKey,
792
- retry: attempt,
793
- },
794
- );
795
- moduleReviews.push(moduleReviewEvent);
796
- } catch (e) {
797
- if (
798
- e instanceof AgenticaValidationError ||
799
- e instanceof AutoBeTimeoutError ||
800
- e instanceof AutoBePreliminaryExhaustedError
801
- ) {
802
- analyzeDebug(
803
- `section per-module-review-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} — skipping this module review`,
804
- );
805
- } else {
806
- throw e;
807
- }
808
- }
809
- }
810
- const reviewEvent = mergeModuleReviewEvents(moduleReviews, fileIndex);
811
- analyzeDebug(
812
- `section per-module-review-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${moduleReviews.length} elapsedMs=${Date.now() - reviewStart}`,
813
- );
814
- perFileReviewResults.set(fileIndex, reviewEvent!);
815
-
816
- return sectionResults;
817
- }),
818
- promptCacheKey,
819
- );
820
-
821
- // Pass 2: Cross-file lightweight review (single call)
822
- crossFileReviewCount++;
823
- if (crossFileReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
824
- analyzeDebug(
825
- `[orchestrateAnalyze] Section stage: skipping cross-file review (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded)`,
826
- );
827
- // Force-pass all pending files
828
- for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
829
- break;
830
- }
831
- analyzeDebug(`section cross-file-validation-start attempt=${attempt}`);
832
- const filesWithSections = props.fileStates
833
- .filter((state) => state.sectionResults !== null)
834
- .map((state) => ({
835
- file: state.file,
836
- sectionEvents: state.sectionResults!,
837
- }));
838
-
839
- // Pass 2a: Programmatic cross-file validation (BEFORE LLM review)
840
- const criticalConflicts = detectConstraintConflicts({
841
- files: filesWithSections,
842
- });
843
- const fileConflictMap: Map<string, string[]> =
844
- buildFileConflictMap(criticalConflicts);
845
-
846
- const attributeDuplicates = detectAttributeDuplicates({
847
- files: filesWithSections,
848
- });
849
- const fileAttributeDuplicateMap: Map<string, string[]> =
850
- buildFileAttributeDuplicateMap(attributeDuplicates);
851
-
852
- const enumConflicts = detectEnumConflicts({
853
- files: filesWithSections,
854
- });
855
- const fileEnumConflictMap: Map<string, string[]> =
856
- buildFileEnumConflictMap(enumConflicts);
857
-
858
- const permissionConflicts = detectPermissionConflicts({
859
- files: filesWithSections,
860
- });
861
- const filePermissionConflictMap: Map<string, string[]> =
862
- buildFilePermissionConflictMap(permissionConflicts);
863
-
864
- const stateFieldConflicts = detectStateFieldConflicts({
865
- files: filesWithSections,
866
- });
867
- const fileStateFieldConflictMap: Map<string, string[]> =
868
- buildFileStateFieldConflictMap(stateFieldConflicts);
869
-
870
- const errorCodeConflicts = detectErrorCodeConflicts({
871
- files: filesWithSections,
872
- });
873
- const fileErrorCodeConflictMap: Map<string, string[]> =
874
- buildFileErrorCodeConflictMap(errorCodeConflicts);
875
-
876
- const proseConflicts = detectProseConstraintConflicts({
877
- files: filesWithSections,
878
- });
879
- const fileProseConflictMap: Map<string, string[]> =
880
- buildFileProseConflictMap(proseConflicts);
881
-
882
- const oversizedTocMap: Map<number, string[]> = new Map();
883
- for (const fileIndex of pendingArray) {
884
- const state = props.fileStates[fileIndex]!;
885
- if (state.file.filename === "00-toc.md" && state.sectionResults) {
886
- const violations = detectOversizedToc(state.sectionResults);
887
- if (violations.length > 0) {
888
- oversizedTocMap.set(fileIndex, violations);
889
- }
890
- }
891
- }
892
-
893
- // Build mechanical violation summary for LLM context
894
- const allMechanicalViolations: string[] = [
895
- ...criticalConflicts.map(
896
- (c) =>
897
- `Constraint conflict: ${c.key} — ${c.values.map((v) => `"${v.display}" in [${v.files.join(", ")}]`).join(" vs ")}`,
898
- ),
899
- ...attributeDuplicates.map(
900
- (d) => `Attribute duplication: ${d.key} in [${d.files.join(", ")}]`,
901
- ),
902
- ...enumConflicts.map(
903
- (c) =>
904
- `Enum conflict: ${c.key} — ${c.values.map((v) => `enum(${v.enumSet}) in [${v.files.join(", ")}]`).join(" vs ")}`,
905
- ),
906
- ...errorCodeConflicts.map(
907
- (c) =>
908
- `Error code conflict: ${c.conditionKey} — ${c.codes.map((cd) => `HTTP ${cd.httpStatus} in [${cd.files.join(", ")}]`).join(" vs ")}`,
909
- ),
910
- ...proseConflicts.map(
911
- (c) =>
912
- `Prose constraint conflict: ${c.entityAttr} — canonical [${c.canonicalValues.join(", ")}] vs prose [${c.proseValues.join(", ")}] in ${c.file}`,
913
- ),
914
- ];
915
- const mechanicalViolationSummary =
916
- allMechanicalViolations.length > 0
917
- ? allMechanicalViolations.join("\n")
918
- : undefined;
919
-
920
- // Pass 2b: Cross-file semantic LLM review (with mechanical violations excluded)
921
- analyzeDebug(`section cross-file-review-start attempt=${attempt}`);
922
- let crossFileReviewEvent: AutoBeAnalyzeSectionReviewEvent | null = null;
923
- try {
924
- crossFileReviewEvent = await orchestrateAnalyzeSectionCrossFileReview(
925
- ctx,
926
- {
927
- scenario: props.scenario,
928
- allFileSummaries: props.fileStates
929
- .filter((s) => s.file.filename !== "00-toc.md")
930
- .map((state) => {
931
- const fi = props.fileStates.indexOf(state);
932
- return {
933
- file: state.file,
934
- moduleEvent: state.moduleResult!,
935
- unitEvents: state.unitResults!,
936
- sectionEvents: state.sectionResults!,
937
- status: pendingIndices.has(fi)
938
- ? attempt === 0
939
- ? ("new" as const)
940
- : ("rewritten" as const)
941
- : ("approved" as const),
942
- };
943
- }),
944
- mechanicalViolationSummary,
945
- progress: props.crossFileSectionReviewProgress,
946
- promptCacheKey,
947
- retry: attempt,
948
- },
949
- );
950
- } catch (e) {
951
- if (
952
- e instanceof AgenticaValidationError ||
953
- e instanceof AutoBeTimeoutError ||
954
- e instanceof AutoBePreliminaryExhaustedError
955
- ) {
956
- analyzeDebug(
957
- `section cross-file-review-force-pass attempt=${attempt} error=${(e as Error).constructor.name} — force-passing all pending files`,
958
- );
959
- for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
960
- break;
961
- }
962
- throw e;
963
- }
964
- analyzeDebug(
965
- `section cross-file-review-done attempt=${attempt} results=${crossFileReviewEvent.fileResults.length}`,
966
- );
967
-
968
- // Merge results from both passes
969
- const crossFileResultMap: Map<
970
- number,
971
- AutoBeAnalyzeSectionReviewFileResult
972
- > = new Map();
973
- const validCrossFileResults = filterValidFileResults(
974
- crossFileReviewEvent.fileResults,
975
- props.fileStates.length,
976
- "Section cross-file review",
977
- );
978
- for (const fr of validCrossFileResults)
979
- crossFileResultMap.set(fr.fileIndex, fr);
980
-
981
- for (const fileIndex of pendingArray) {
982
- const state: IFileState = props.fileStates[fileIndex]!;
983
-
984
- // Increment review count and force-pass if exceeded limit
985
- state.sectionReviewCount = (state.sectionReviewCount ?? 0) + 1;
986
- if (state.sectionReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
987
- analyzeDebug(
988
- `[orchestrateAnalyze] Section stage: force-passing (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded) for file "${state.file.filename}"`,
989
- );
990
- pendingIndices.delete(fileIndex);
991
- continue;
992
- }
993
-
994
- const perFileEvent = perFileReviewResults.get(fileIndex);
995
- const perFileResult = perFileEvent?.fileResults[0];
996
- const crossFileResult = crossFileResultMap.get(fileIndex);
997
-
998
- const perFileApproved = perFileResult?.approved ?? true;
999
- const crossFileApproved = crossFileResult?.approved ?? true;
1000
-
1001
- // Check if this file has programmatically-detected critical conflicts
1002
- const filename = state.file.filename;
1003
- const fileCriticalConflicts = fileConflictMap.get(filename) ?? [];
1004
- const fileAttrDuplicates = fileAttributeDuplicateMap.get(filename) ?? [];
1005
- const fileEnumConflicts = fileEnumConflictMap.get(filename) ?? [];
1006
- const filePermissionConflicts =
1007
- filePermissionConflictMap.get(filename) ?? [];
1008
- const fileStateFieldConflicts =
1009
- fileStateFieldConflictMap.get(filename) ?? [];
1010
- const fileErrorCodeConflicts =
1011
- fileErrorCodeConflictMap.get(filename) ?? [];
1012
- const fileOversizedToc = oversizedTocMap.get(fileIndex) ?? [];
1013
- const fileProseConflicts = fileProseConflictMap.get(filename) ?? [];
1014
- const hasCriticalConflict =
1015
- fileCriticalConflicts.length > 0 ||
1016
- fileAttrDuplicates.length > 0 ||
1017
- fileEnumConflicts.length > 0 ||
1018
- filePermissionConflicts.length > 0 ||
1019
- fileStateFieldConflicts.length > 0 ||
1020
- fileErrorCodeConflicts.length > 0 ||
1021
- fileOversizedToc.length > 0 ||
1022
- fileProseConflicts.length > 0;
1023
-
1024
- // Decision logic:
1025
- // 1. per-file reject → reject (unchanged)
1026
- // 2. per-file approve + critical conflict detected → reject (NEW: patch-first)
1027
- // 3. per-file approve + no critical conflict → approve (unchanged)
1028
- const approved = perFileApproved && !hasCriticalConflict;
1029
-
1030
- const structuredPerFileIssues =
1031
- collectStructuredReviewIssues(perFileResult);
1032
- const structuredCrossFileIssues =
1033
- collectStructuredReviewIssues(crossFileResult);
1034
- const programmaticIssues = buildProgrammaticSectionIssues({
1035
- fileCriticalConflicts,
1036
- fileAttrDuplicates,
1037
- fileEnumConflicts,
1038
- filePermissionConflicts,
1039
- fileStateFieldConflicts,
1040
- fileErrorCodeConflicts,
1041
- fileOversizedToc,
1042
- fileProseConflicts,
1043
- });
1044
-
1045
- if (approved) {
1046
- // NOTE: revisedSections intentionally ignored — approved means pass as-is.
1047
- // Applying revisedSections caused infinite re-write loops (sections kept growing).
1048
- // Pass cross-file feedback as advisory for next retry's context
1049
- if (!crossFileApproved && crossFileResult?.feedback) {
1050
- state.sectionFeedback = `[Cross-file advisory] ${crossFileResult.feedback}`;
1051
- }
1052
- state.sectionRetryCount = 0;
1053
- state.sectionStagnationCount = 0;
1054
- state.lastSectionContentSignature = undefined;
1055
- state.lastSectionRejectionSignature = undefined;
1056
- pendingIndices.delete(fileIndex);
1057
- } else if (!perFileApproved) {
1058
- // Per-file rejected: store only the latest per-file feedback (no accumulation)
1059
- state.sectionFeedback = formatStructuredIssuesForRetry({
1060
- fallbackFeedback: perFileResult?.feedback ?? "",
1061
- issues: structuredPerFileIssues,
1062
- });
1063
-
1064
- // Use only per-file rejectedModuleUnits (no cross-file merge)
1065
- state.rejectedModuleUnits = normalizeRejectedModuleUnits(
1066
- perFileResult?.rejectedModuleUnits ?? null,
1067
- structuredPerFileIssues,
1068
- );
1069
- // Fallback: infer targets from issues to avoid full-file rewrite
1070
- if (state.rejectedModuleUnits === null) {
1071
- state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
1072
- structuredPerFileIssues,
1073
- state.unitResults!,
1074
- );
1075
- }
1076
- analyzeDebug(
1077
- `section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
1078
- state.rejectedModuleUnits,
1079
- )} issues=${formatReviewIssuesSummary(structuredPerFileIssues)} feedback=${truncateForDebug(
1080
- state.sectionFeedback ?? "",
1081
- 500,
1082
- )}`,
1083
- );
1084
- } else {
1085
- // Critical conflict rejected (per-file approved but programmatic violations exist)
1086
- // Use cross-file rejectedModuleUnits for targeted patch if available
1087
- state.sectionFeedback = formatStructuredIssuesForRetry({
1088
- fallbackFeedback:
1089
- `[Critical conflict] ${[
1090
- ...fileCriticalConflicts,
1091
- ...fileAttrDuplicates,
1092
- ...fileEnumConflicts,
1093
- ...fileProseConflicts,
1094
- ].join("; ")}` +
1095
- (crossFileResult?.feedback ? `\n${crossFileResult.feedback}` : ""),
1096
- issues: [...programmaticIssues, ...structuredCrossFileIssues],
1097
- });
1098
- state.rejectedModuleUnits = normalizeRejectedModuleUnits(
1099
- crossFileResult?.rejectedModuleUnits ?? null,
1100
- [...programmaticIssues, ...structuredCrossFileIssues],
1101
- );
1102
- // Fallback: infer targets from issues to avoid full-file rewrite
1103
- if (state.rejectedModuleUnits === null) {
1104
- state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
1105
- [...programmaticIssues, ...structuredCrossFileIssues],
1106
- state.unitResults!,
1107
- );
1108
- }
1109
- analyzeDebug(
1110
- `section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
1111
- state.rejectedModuleUnits,
1112
- )} issues=${formatReviewIssuesSummary([
1113
- ...programmaticIssues,
1114
- ...structuredCrossFileIssues,
1115
- ])} feedback=${truncateForDebug(state.sectionFeedback ?? "", 500)}`,
1116
- );
1117
- }
1118
-
1119
- if (!approved) {
1120
- const contentSignature = buildSectionContentSignature(state);
1121
- const rejectionSignature = buildSectionRejectionSignature({
1122
- rejectedModuleUnits: state.rejectedModuleUnits ?? null,
1123
- feedback: state.sectionFeedback ?? "",
1124
- });
1125
- const isStagnant =
1126
- state.lastSectionContentSignature === contentSignature &&
1127
- state.lastSectionRejectionSignature === rejectionSignature;
1128
- state.sectionStagnationCount = isStagnant
1129
- ? (state.sectionStagnationCount ?? 0) + 1
1130
- : 0;
1131
- state.sectionRetryCount = (state.sectionRetryCount ?? 0) + 1;
1132
- state.lastSectionContentSignature = contentSignature;
1133
- state.lastSectionRejectionSignature = rejectionSignature;
1134
-
1135
- if ((state.sectionRetryCount ?? 0) > ANALYZE_SECTION_FILE_MAX_RETRY) {
1136
- analyzeDebug(
1137
- `[orchestrateAnalyze] Section stage: force-passing (max retry exceeded: ${ANALYZE_SECTION_FILE_MAX_RETRY}) for file "${state.file.filename}"`,
1138
- );
1139
- pendingIndices.delete(fileIndex);
1140
- continue;
1141
- }
1142
- if (
1143
- (state.sectionStagnationCount ?? 0) >= ANALYZE_SECTION_STAGNATION_MAX
1144
- ) {
1145
- analyzeDebug(
1146
- `[orchestrateAnalyze] Section stage: force-passing (stagnation detected ${state.sectionStagnationCount}x) for file "${state.file.filename}"`,
1147
- );
1148
- pendingIndices.delete(fileIndex);
1149
- continue;
1150
- }
1151
- }
1152
- }
1153
- }
1154
-
1155
- if (pendingIndices.size > 0) {
1156
- analyzeDebug(
1157
- `[orchestrateAnalyze] Section stage: force-passing after max retries for files: ${[
1158
- ...pendingIndices,
1159
- ]
1160
- .map((i) => props.fileStates[i]!.file.filename)
1161
- .join(", ")}`,
1162
- );
1163
- }
1164
- }
1165
-
1166
- // ─── Section-stage helper functions ───
1167
-
1168
- function computeSectionBatchSize(props: {
1169
- attempt: number;
1170
- pendingCount: number;
1171
- }): number {
1172
- return Math.min(8, props.pendingCount);
1173
- }
1174
-
1175
- function chunkSectionFileIndices(indices: number[], size: number): number[][] {
1176
- if (indices.length === 0) return [];
1177
- if (size <= 0 || size >= indices.length) return [indices];
1178
- const chunks: number[][] = [];
1179
- for (let i = 0; i < indices.length; i += size)
1180
- chunks.push(indices.slice(i, i + size));
1181
- return chunks;
1182
- }
1183
-
1184
- function buildRejectedSet(
1185
- rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1186
- ): Set<string> | null {
1187
- if (rejected == null) return null;
1188
- if (rejected.length === 0) return null;
1189
- const set: Set<string> = new Set();
1190
- for (const entry of rejected) {
1191
- for (const ui of entry.unitIndices) {
1192
- set.add(`${entry.moduleIndex}:${ui}`);
1193
- }
1194
- }
1195
- return set.size > 0 ? set : null;
1196
- }
1197
-
1198
- interface ISectionAwareFeedback {
1199
- feedback: string;
1200
- sectionIndices: number[] | null;
1201
- }
1202
-
1203
- function buildFeedbackMap(
1204
- rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1205
- ): Map<string, ISectionAwareFeedback> {
1206
- const map: Map<string, ISectionAwareFeedback> = new Map();
1207
- if (rejected == null) return map;
1208
- for (const entry of rejected) {
1209
- for (const ui of entry.unitIndices) {
1210
- map.set(`${entry.moduleIndex}:${ui}`, {
1211
- feedback: formatRejectedModuleUnitFeedback(entry, ui),
1212
- sectionIndices: entry.sectionIndicesPerUnit?.[ui] ?? null,
1213
- });
1214
- }
1215
- }
1216
- return map;
1217
- }
1218
-
1219
- function isSectionRejected(
1220
- rejectedSet: Set<string> | null,
1221
- moduleIndex: number,
1222
- unitIndex: number,
1223
- ): boolean {
1224
- if (rejectedSet === null) return true;
1225
- return rejectedSet.has(`${moduleIndex}:${unitIndex}`);
1226
- }
1227
-
1228
- function filterValidFileResults<T extends { fileIndex: number }>(
1229
- fileResults: T[],
1230
- fileCount: number,
1231
- stage: string,
1232
- ): T[] {
1233
- return fileResults.filter((fr) => {
1234
- if (
1235
- Number.isInteger(fr.fileIndex) &&
1236
- fr.fileIndex >= 0 &&
1237
- fr.fileIndex < fileCount
1238
- ) {
1239
- return true;
1240
- }
1241
- console.warn(
1242
- `[orchestrateAnalyze] ${stage}: invalid fileIndex ${fr.fileIndex} (valid: 0-${fileCount - 1})`,
1243
- );
1244
- return false;
1245
- });
1246
- }
1247
-
1248
- function formatRejectedModuleUnitFeedback(
1249
- entry: AutoBeAnalyzeSectionReviewRejectedModuleUnit,
1250
- unitIndex: number,
1251
- ): string {
1252
- const scopedIssues = (entry.issues ?? []).filter(
1253
- (issue) =>
1254
- issue.moduleIndex === entry.moduleIndex &&
1255
- (issue.unitIndex === null || issue.unitIndex === unitIndex),
1256
- );
1257
- if (scopedIssues.length === 0) return entry.feedback;
1258
- return [
1259
- entry.feedback,
1260
- ...scopedIssues.map(
1261
- (issue) =>
1262
- `- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}`,
1263
- ),
1264
- ].join("\n");
1265
- }
1266
-
1267
- function collectStructuredReviewIssues(
1268
- result:
1269
- | {
1270
- feedback: string;
1271
- rejectedModuleUnits?:
1272
- | AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
1273
- | null;
1274
- issues?: AutoBeAnalyzeSectionReviewIssue[] | null;
1275
- }
1276
- | undefined,
1277
- ): AutoBeAnalyzeSectionReviewIssue[] {
1278
- if (!result) return [];
1279
- const collected: AutoBeAnalyzeSectionReviewIssue[] = [];
1280
-
1281
- for (const issue of result.issues ?? []) collected.push(issue);
1282
- for (const group of result.rejectedModuleUnits ?? []) {
1283
- for (const issue of group.issues ?? []) collected.push(issue);
1284
- if ((group.issues?.length ?? 0) === 0) {
1285
- for (const unitIndex of group.unitIndices) {
1286
- collected.push({
1287
- ruleCode: "section_review_reject",
1288
- moduleIndex: group.moduleIndex,
1289
- unitIndex,
1290
- fixInstruction:
1291
- group.feedback || result.feedback || "Fix review issues.",
1292
- evidence: null,
1293
- });
1294
- }
1295
- }
1296
- }
1297
-
1298
- if (collected.length === 0 && result.feedback.trim().length > 0) {
1299
- collected.push({
1300
- ruleCode: "section_review_reject",
1301
- moduleIndex: null,
1302
- unitIndex: null,
1303
- fixInstruction: result.feedback,
1304
- evidence: null,
1305
- });
1306
- }
1307
- return dedupeReviewIssues(collected);
1308
- }
1309
-
1310
- function buildProgrammaticSectionIssues(props: {
1311
- fileCriticalConflicts: string[];
1312
- fileAttrDuplicates: string[];
1313
- fileEnumConflicts: string[];
1314
- filePermissionConflicts: string[];
1315
- fileStateFieldConflicts: string[];
1316
- fileErrorCodeConflicts: string[];
1317
- fileOversizedToc: string[];
1318
- fileProseConflicts: string[];
1319
- }): AutoBeAnalyzeSectionReviewIssue[] {
1320
- return [
1321
- ...props.fileCriticalConflicts.map((detail) => ({
1322
- ruleCode: "cross_file_constraint_conflict",
1323
- moduleIndex: null,
1324
- unitIndex: null,
1325
- fixInstruction:
1326
- "Align conflicting constraints/values with other files and preserve one canonical value.",
1327
- evidence: detail,
1328
- })),
1329
- ...props.fileAttrDuplicates.map((detail) => ({
1330
- ruleCode: "cross_file_attribute_duplicate",
1331
- moduleIndex: null,
1332
- unitIndex: null,
1333
- fixInstruction:
1334
- "Remove duplicate attribute specifications across files and keep ownership in one file.",
1335
- evidence: detail,
1336
- })),
1337
- ...props.fileEnumConflicts.map((detail) => ({
1338
- ruleCode: "cross_file_enum_conflict",
1339
- moduleIndex: null,
1340
- unitIndex: null,
1341
- fixInstruction:
1342
- "Align enum values with the canonical definition from the first file that specified this attribute. Use the exact same enum set.",
1343
- evidence: detail,
1344
- })),
1345
- ...props.filePermissionConflicts.map((detail) => ({
1346
- ruleCode: "cross_file_permission_conflict",
1347
- moduleIndex: null,
1348
- unitIndex: null,
1349
- fixInstruction:
1350
- "Align permission rules with the canonical definition. If the first file says 'denied', all files must say 'denied' for the same actor→operation.",
1351
- evidence: detail,
1352
- })),
1353
- ...props.fileStateFieldConflicts.map((detail) => ({
1354
- ruleCode: "cross_file_state_field_conflict",
1355
- moduleIndex: null,
1356
- unitIndex: null,
1357
- fixInstruction:
1358
- "Use ONE canonical approach for state fields. If other files use 'deletedAt: datetime', do NOT use 'isDeleted: boolean'. Pick one and align.",
1359
- evidence: detail,
1360
- })),
1361
- ...props.fileErrorCodeConflicts.map((detail) => ({
1362
- ruleCode: "cross_file_error_code_conflict",
1363
- moduleIndex: null,
1364
- unitIndex: null,
1365
- fixInstruction:
1366
- "Use the canonical error code defined in the first file. Do NOT invent alternative error codes for the same condition.",
1367
- evidence: detail,
1368
- })),
1369
- ...props.fileOversizedToc.map((detail) => ({
1370
- ruleCode: "oversized_toc",
1371
- moduleIndex: null,
1372
- unitIndex: null,
1373
- fixInstruction:
1374
- "TOC must be a concise navigation aid. Remove detailed requirements, keep only navigation tables and brief summaries.",
1375
- evidence: detail,
1376
- })),
1377
- ...props.fileProseConflicts.map((detail) => ({
1378
- ruleCode: "cross_file_prose_constraint_conflict",
1379
- moduleIndex: null,
1380
- unitIndex: null,
1381
- fixInstruction:
1382
- "Remove the restated constraint value and use a backtick reference to the canonical definition in 02-domain-model instead. Example: 'THE system SHALL validate `User.bio` per entity constraints (see 02-domain-model)'",
1383
- evidence: detail,
1384
- })),
1385
- ];
1386
- }
1387
-
1388
- function buildSectionIndicesPerUnit(
1389
- issues: AutoBeAnalyzeSectionReviewIssue[],
1390
- moduleIndex: number,
1391
- unitIndices: number[],
1392
- ): Record<number, number[]> | null {
1393
- const map: Record<number, Set<number>> = {};
1394
- let hasSectionLevel = false;
1395
-
1396
- for (const issue of issues) {
1397
- if (
1398
- issue.moduleIndex === moduleIndex &&
1399
- issue.unitIndex !== null &&
1400
- unitIndices.includes(issue.unitIndex) &&
1401
- issue.sectionIndex !== null &&
1402
- issue.sectionIndex !== undefined &&
1403
- Number.isInteger(issue.sectionIndex) &&
1404
- issue.sectionIndex >= 0
1405
- ) {
1406
- if (!map[issue.unitIndex]) map[issue.unitIndex] = new Set();
1407
- map[issue.unitIndex]!.add(issue.sectionIndex);
1408
- hasSectionLevel = true;
1409
- }
1410
- }
1411
-
1412
- if (!hasSectionLevel) return null;
1413
-
1414
- const result: Record<number, number[]> = {};
1415
- for (const [ui, sectionSet] of Object.entries(map)) {
1416
- result[Number(ui)] = [...sectionSet].sort((a, b) => a - b);
1417
- }
1418
- return result;
1419
- }
1420
-
1421
- function normalizeRejectedModuleUnits(
1422
- rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1423
- fileIssues: AutoBeAnalyzeSectionReviewIssue[],
1424
- ): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
1425
- if (rejected == null) return null;
1426
- return rejected.map((entry) => {
1427
- const enrichedIssues =
1428
- (entry.issues?.length ?? 0) > 0
1429
- ? dedupeReviewIssues(entry.issues ?? [])
1430
- : dedupeReviewIssues(
1431
- fileIssues.filter(
1432
- (issue) =>
1433
- issue.moduleIndex === entry.moduleIndex &&
1434
- (issue.unitIndex === null ||
1435
- entry.unitIndices.includes(issue.unitIndex)),
1436
- ),
1437
- );
1438
-
1439
- const sectionIndicesPerUnit =
1440
- entry.sectionIndicesPerUnit ??
1441
- buildSectionIndicesPerUnit(
1442
- enrichedIssues,
1443
- entry.moduleIndex,
1444
- entry.unitIndices,
1445
- );
1446
-
1447
- return {
1448
- ...entry,
1449
- issues: enrichedIssues,
1450
- sectionIndicesPerUnit,
1451
- };
1452
- });
1453
- }
1454
-
1455
- /**
1456
- * Infer rejectedModuleUnits from structured issues when the LLM review didn't
1457
- * provide explicit rejection targets. This prevents full-file rewrites when
1458
- * only specific module/unit pairs have issues.
1459
- */
1460
- function inferRejectedModuleUnitsFromIssues(
1461
- issues: AutoBeAnalyzeSectionReviewIssue[],
1462
- unitResults: AutoBeAnalyzeWriteUnitEvent[],
1463
- ): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
1464
- const moduleUnitMap = new Map<number, Set<number>>();
1465
- let hasTargetedIssue = false;
1466
-
1467
- for (const issue of issues) {
1468
- if (issue.moduleIndex !== null && issue.moduleIndex !== undefined) {
1469
- hasTargetedIssue = true;
1470
- if (!moduleUnitMap.has(issue.moduleIndex)) {
1471
- moduleUnitMap.set(issue.moduleIndex, new Set());
1472
- }
1473
- if (issue.unitIndex !== null && issue.unitIndex !== undefined) {
1474
- moduleUnitMap.get(issue.moduleIndex)!.add(issue.unitIndex);
1475
- }
1476
- }
1477
- }
1478
-
1479
- if (!hasTargetedIssue) return null;
1480
-
1481
- const result: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] = [];
1482
- for (const [moduleIndex, unitIndexSet] of moduleUnitMap) {
1483
- // If no specific units targeted, include all units for this module
1484
- let unitIndices: number[];
1485
- if (unitIndexSet.size === 0) {
1486
- const unitEvent = unitResults[moduleIndex];
1487
- unitIndices = unitEvent
1488
- ? Array.from({ length: unitEvent.unitSections.length }, (_, i) => i)
1489
- : [];
1490
- } else {
1491
- unitIndices = [...unitIndexSet].sort((a, b) => a - b);
1492
- }
1493
-
1494
- const moduleIssues = dedupeReviewIssues(
1495
- issues.filter(
1496
- (i) =>
1497
- i.moduleIndex === moduleIndex &&
1498
- (i.unitIndex === null || unitIndices.includes(i.unitIndex)),
1499
- ),
1500
- );
1501
-
1502
- result.push({
1503
- moduleIndex,
1504
- unitIndices,
1505
- feedback: moduleIssues.map((i) => i.fixInstruction).join("; "),
1506
- issues: moduleIssues,
1507
- sectionIndicesPerUnit: buildSectionIndicesPerUnit(
1508
- moduleIssues,
1509
- moduleIndex,
1510
- unitIndices,
1511
- ),
1512
- });
1513
- }
1514
-
1515
- return result.length > 0 ? result : null;
1516
- }
1517
-
1518
- function formatStructuredIssuesForRetry(props: {
1519
- fallbackFeedback: string;
1520
- issues: AutoBeAnalyzeSectionReviewIssue[];
1521
- }): string {
1522
- if (props.issues.length === 0) return props.fallbackFeedback;
1523
- const lines = props.issues.map(
1524
- (issue) =>
1525
- `- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}` +
1526
- (issue.evidence ? ` | evidence=${issue.evidence}` : ""),
1527
- );
1528
- return `${props.fallbackFeedback}\n\n[STRUCTURED REVIEW ISSUES]\n${lines.join("\n")}`.trim();
1529
- }
1530
-
1531
- function formatIssueTarget(
1532
- issue: Pick<AutoBeAnalyzeSectionReviewIssue, "moduleIndex" | "unitIndex"> &
1533
- Partial<Pick<AutoBeAnalyzeSectionReviewIssue, "sectionIndex">>,
1534
- ): string {
1535
- const parts: string[] = [];
1536
- if (issue.moduleIndex !== null && issue.moduleIndex !== undefined)
1537
- parts.push(`m${issue.moduleIndex}`);
1538
- if (issue.unitIndex !== null && issue.unitIndex !== undefined)
1539
- parts.push(`u${issue.unitIndex}`);
1540
- if (issue.sectionIndex !== null && issue.sectionIndex !== undefined)
1541
- parts.push(`s${issue.sectionIndex}`);
1542
- return parts.length ? parts.join(".") : "file";
1543
- }
1544
-
1545
- function dedupeReviewIssues(
1546
- issues: AutoBeAnalyzeSectionReviewIssue[],
1547
- ): AutoBeAnalyzeSectionReviewIssue[] {
1548
- const map = new Map<string, AutoBeAnalyzeSectionReviewIssue>();
1549
- for (const issue of issues) {
1550
- const key = [
1551
- issue.ruleCode,
1552
- issue.moduleIndex ?? "x",
1553
- issue.unitIndex ?? "x",
1554
- issue.sectionIndex ?? "x",
1555
- issue.fixInstruction,
1556
- ].join("|");
1557
- if (!map.has(key)) map.set(key, issue);
1558
- }
1559
- return [...map.values()];
1560
- }
1561
-
1562
- // ─── Per-module review helpers ───
1563
-
1564
- function buildSiblingModuleSummaries(
1565
- moduleEvent: AutoBeAnalyzeWriteModuleEvent,
1566
- sectionResults: AutoBeAnalyzeWriteSectionEvent[][],
1567
- excludeModuleIndex: number,
1568
- ): Array<{
1569
- moduleIndex: number;
1570
- title: string;
1571
- sectionTitles: string[];
1572
- }> {
1573
- return sectionResults
1574
- .map((sectionsForModule, moduleIndex) => ({
1575
- moduleIndex,
1576
- title: moduleEvent.moduleSections[moduleIndex]?.title ?? "Unknown",
1577
- sectionTitles: sectionsForModule.flatMap((se) =>
1578
- se.sectionSections.map((s) => s.title),
1579
- ),
1580
- }))
1581
- .filter((s) => s.moduleIndex !== excludeModuleIndex);
1582
- }
1583
-
1584
- function mergeModuleReviewEvents(
1585
- moduleReviews: AutoBeAnalyzeSectionReviewEvent[],
1586
- fileIndex: number,
1587
- ): AutoBeAnalyzeSectionReviewEvent | null {
1588
- if (moduleReviews.length === 0) return null;
1589
-
1590
- const allApproved = moduleReviews.every(
1591
- (r) => r.fileResults[0]?.approved ?? true,
1592
- );
1593
- const allFeedback = moduleReviews
1594
- .map((r) => r.fileResults[0]?.feedback)
1595
- .filter(Boolean)
1596
- .join("\n");
1597
- const allRejectedModuleUnits = moduleReviews.flatMap(
1598
- (r) => r.fileResults[0]?.rejectedModuleUnits ?? [],
1599
- );
1600
- const allIssues = moduleReviews.flatMap(
1601
- (r) => r.fileResults[0]?.issues ?? [],
1602
- );
1603
-
1604
- // Use the last review event as base (for tokenUsage, metric, etc.)
1605
- const base = moduleReviews[moduleReviews.length - 1]!;
1606
- return {
1607
- ...base,
1608
- fileResults: [
1609
- {
1610
- fileIndex,
1611
- approved: allApproved,
1612
- feedback: allFeedback,
1613
- revisedSections: null,
1614
- rejectedModuleUnits:
1615
- allRejectedModuleUnits.length > 0 ? allRejectedModuleUnits : null,
1616
- issues: allIssues.length > 0 ? allIssues : null,
1617
- },
1618
- ],
1619
- };
1620
- }
1621
-
1622
- // ─── Section-stage helpers ───
1623
-
1624
- function buildSectionContentSignature(state: IFileState): string {
1625
- if (!state.sectionResults) return "none";
1626
- return JSON.stringify(
1627
- state.sectionResults.map((moduleSections) =>
1628
- moduleSections.map((unit) =>
1629
- unit.sectionSections.map((section) => ({
1630
- title: section.title,
1631
- // content text included to detect no-progress rewrites
1632
- content: section.content,
1633
- })),
1634
- ),
1635
- ),
1636
- );
1637
- }
1638
-
1639
- function buildSectionRejectionSignature(props: {
1640
- rejectedModuleUnits:
1641
- | AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
1642
- | null
1643
- | undefined;
1644
- feedback: string;
1645
- }): string {
1646
- return JSON.stringify({
1647
- rejectedModuleUnits: (props.rejectedModuleUnits ?? []).map((entry) => ({
1648
- moduleIndex: entry.moduleIndex,
1649
- unitIndices: [...entry.unitIndices].sort((a, b) => a - b),
1650
- feedback: entry.feedback,
1651
- issues: (entry.issues ?? []).map((issue) => ({
1652
- ruleCode: issue.ruleCode,
1653
- moduleIndex: issue.moduleIndex,
1654
- unitIndex: issue.unitIndex,
1655
- sectionIndex: issue.sectionIndex ?? null,
1656
- fixInstruction: issue.fixInstruction,
1657
- })),
1658
- })),
1659
- feedback: props.feedback,
1660
- });
1661
- }
1662
-
1663
- function formatRejectedModuleUnitsSummary(
1664
- rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1665
- ): string {
1666
- if (!rejected || rejected.length === 0) return "all-or-unknown";
1667
- return rejected
1668
- .slice(0, 6)
1669
- .map((entry) => {
1670
- const unitParts = entry.unitIndices.map((ui) => {
1671
- const sectionPart = entry.sectionIndicesPerUnit?.[ui];
1672
- return sectionPart ? `u${ui}(s${sectionPart.join(",s")})` : `u${ui}`;
1673
- });
1674
- return `m${entry.moduleIndex}:${unitParts.join(",") || "-"}`;
1675
- })
1676
- .join(" | ");
1677
- }
1678
-
1679
- function formatReviewIssuesSummary(
1680
- issues: AutoBeAnalyzeSectionReviewIssue[],
1681
- ): string {
1682
- if (issues.length === 0) return "none";
1683
- return issues
1684
- .slice(0, 8)
1685
- .map((issue) => `${issue.ruleCode}@${formatIssueTarget(issue)}`)
1686
- .join(", ");
1687
- }
1688
-
1689
- function truncateForDebug(text: string, max: number): string {
1690
- const singleLine = text.replace(/\s+/g, " ").trim();
1691
- if (singleLine.length <= max) return singleLine;
1692
- return `${singleLine.slice(0, max)}...`;
1693
- }
1
+ import { AgenticaValidationError } from "@agentica/core";
2
+ import {
3
+ AutoBeAnalyze,
4
+ AutoBeAnalyzeHistory,
5
+ AutoBeAnalyzeScenarioEvent,
6
+ AutoBeAnalyzeScenarioReviewEvent,
7
+ AutoBeAnalyzeSectionReviewEvent,
8
+ AutoBeAnalyzeSectionReviewFileResult,
9
+ AutoBeAnalyzeSectionReviewIssue,
10
+ AutoBeAnalyzeSectionReviewRejectedModuleUnit,
11
+ AutoBeAnalyzeWriteModuleEvent,
12
+ AutoBeAnalyzeWriteSectionEvent,
13
+ AutoBeAnalyzeWriteUnitEvent,
14
+ AutoBeAssistantMessageHistory,
15
+ AutoBeProgressEventBase,
16
+ } from "@autobe/interface";
17
+ import { v7 } from "uuid";
18
+
19
+ import { AutoBeConfigConstant } from "../../constants/AutoBeConfigConstant";
20
+ import { AutoBeContext } from "../../context/AutoBeContext";
21
+ import { AutoBePreliminaryExhaustedError } from "../../utils/AutoBePreliminaryExhaustedError";
22
+ import { AutoBeTimeoutError } from "../../utils/AutoBeTimeoutError";
23
+ import { executeCachedBatch } from "../../utils/executeCachedBatch";
24
+ import { fillTocDeterministic } from "./fillTocDeterministic";
25
+ import { orchestrateAnalyzeScenario } from "./orchestrateAnalyzeScenario";
26
+ import { orchestrateAnalyzeScenarioReview } from "./orchestrateAnalyzeScenarioReview";
27
+ import { orchestrateAnalyzeSectionCrossFileReview } from "./orchestrateAnalyzeSectionCrossFileReview";
28
+ import { orchestrateAnalyzeSectionReview } from "./orchestrateAnalyzeSectionReview";
29
+ import { orchestrateAnalyzeWriteSection } from "./orchestrateAnalyzeWriteSection";
30
+ import { orchestrateAnalyzeWriteSectionPatch } from "./orchestrateAnalyzeWriteSectionPatch";
31
+ import { orchestrateAnalyzeWriteUnit } from "./orchestrateAnalyzeWriteUnit";
32
+ import {
33
+ assembleContent,
34
+ assembleModule,
35
+ } from "./programmers/AutoBeAnalyzeProgrammer";
36
+ import {
37
+ FixedAnalyzeTemplateFeature,
38
+ FixedAnalyzeTemplateUnitTemplate,
39
+ buildFixedAnalyzeExpandedTemplate,
40
+ expandFixedAnalyzeTemplateUnits,
41
+ } from "./structures/FixedAnalyzeTemplate";
42
+ import {
43
+ buildFileAttributeDuplicateMap,
44
+ buildFileConflictMap,
45
+ buildFileEnumConflictMap,
46
+ buildFilePermissionConflictMap,
47
+ buildFileStateFieldConflictMap,
48
+ detectAttributeDuplicates,
49
+ detectConstraintConflicts,
50
+ detectEnumConflicts,
51
+ detectPermissionConflicts,
52
+ detectStateFieldConflicts,
53
+ } from "./utils/buildConstraintConsistencyReport";
54
+ import {
55
+ buildFileErrorCodeConflictMap,
56
+ detectErrorCodeConflicts,
57
+ } from "./utils/buildErrorCodeRegistry";
58
+ import { detectOversizedToc } from "./utils/buildHardValidators";
59
+ import {
60
+ buildFileProseConflictMap,
61
+ detectProseConstraintConflicts,
62
+ } from "./utils/detectProseConstraintConflicts";
63
+ import { validateScenarioBasics } from "./utils/validateScenarioBasics";
64
+
65
+ /**
66
+ * Per-file state tracking across all three stages (Module → Unit → Section).
67
+ *
68
+ * Maintains each file's intermediate results and cross-file review feedback
69
+ * throughout the stage-synchronized pipeline.
70
+ */
71
+ interface IFileState {
72
+ file: AutoBeAnalyze.IFileScenario;
73
+ moduleResult: AutoBeAnalyzeWriteModuleEvent | null;
74
+ unitResults: AutoBeAnalyzeWriteUnitEvent[] | null;
75
+ sectionResults: AutoBeAnalyzeWriteSectionEvent[][] | null;
76
+ sectionFeedback?: string;
77
+ // Section-stage partial regeneration tracking
78
+ rejectedModuleUnits?: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null;
79
+ sectionRetryCount?: number;
80
+ sectionReviewCount?: number;
81
+ sectionStagnationCount?: number;
82
+ lastSectionContentSignature?: string;
83
+ lastSectionRejectionSignature?: string;
84
+ }
85
+
86
+ const ANALYZE_SCENARIO_MAX_RETRY = 2;
87
+ const ANALYZE_SECTION_FILE_MAX_RETRY = 5;
88
+ const ANALYZE_SECTION_FILE_MAX_REVIEW = 2;
89
+ const ANALYZE_SECTION_STAGNATION_MAX = 4;
90
+ const ANALYZE_DEBUG_LOG = process.env.AUTOBE_DEBUG_ANALYZE === "1";
91
+
92
+ const analyzeDebug = (message: string): void => {
93
+ if (!ANALYZE_DEBUG_LOG) return;
94
+ console.log(`[analyze-debug] ${new Date().toISOString()} ${message}`);
95
+ };
96
+
97
+ export const orchestrateAnalyze = async (
98
+ ctx: AutoBeContext,
99
+ ): Promise<AutoBeAssistantMessageHistory | AutoBeAnalyzeHistory> => {
100
+ // Initialize analysis state
101
+ const step: number = (ctx.state().analyze?.step ?? -1) + 1;
102
+ const startTime: Date = new Date();
103
+
104
+ ctx.dispatch({
105
+ type: "analyzeStart",
106
+ id: v7(),
107
+ step,
108
+ created_at: startTime.toISOString(),
109
+ });
110
+
111
+ // Generate analysis scenario with pre-check + LLM review + retry loop
112
+ let scenario!: AutoBeAnalyzeScenarioEvent;
113
+ let scenarioFeedback: string | undefined;
114
+
115
+ for (let attempt = 0; attempt <= ANALYZE_SCENARIO_MAX_RETRY; attempt++) {
116
+ const rawScenario = await orchestrateAnalyzeScenario(ctx, {
117
+ feedback: scenarioFeedback,
118
+ });
119
+ if (rawScenario.type === "assistantMessage")
120
+ return ctx.assistantMessage(rawScenario);
121
+
122
+ // 1) Programmatic pre-check
123
+ const preCheck = validateScenarioBasics({
124
+ prefix: rawScenario.prefix,
125
+ actors: rawScenario.actors,
126
+ entities: rawScenario.entities,
127
+ });
128
+ if (!preCheck.valid && attempt < ANALYZE_SCENARIO_MAX_RETRY) {
129
+ analyzeDebug(
130
+ `Scenario pre-check failed (attempt ${attempt}): ${preCheck.errors.join("; ")}`,
131
+ );
132
+ scenarioFeedback = `Programmatic validation failed:\n${preCheck.errors.join("\n")}`;
133
+ continue;
134
+ }
135
+
136
+ // 2) LLM review
137
+ let review: AutoBeAnalyzeScenarioReviewEvent;
138
+ try {
139
+ review = await orchestrateAnalyzeScenarioReview(ctx, {
140
+ scenario: rawScenario,
141
+ retry: attempt,
142
+ });
143
+ } catch (e) {
144
+ if (
145
+ e instanceof AgenticaValidationError ||
146
+ e instanceof AutoBePreliminaryExhaustedError ||
147
+ e instanceof AutoBeTimeoutError
148
+ ) {
149
+ analyzeDebug(
150
+ `scenario review force-pass (attempt ${attempt}) error=${(e as Error).constructor.name}`,
151
+ );
152
+ review = {
153
+ type: "analyzeScenarioReview",
154
+ id: v7(),
155
+ approved: true,
156
+ feedback:
157
+ "Review could not be completed; proceeding with current scenario.",
158
+ issues: [],
159
+ tokenUsage: {
160
+ total: 0,
161
+ input: { total: 0, cached: 0 },
162
+ output: {
163
+ total: 0,
164
+ reasoning: 0,
165
+ accepted_prediction: 0,
166
+ rejected_prediction: 0,
167
+ },
168
+ },
169
+ metric: {
170
+ attempt: 0,
171
+ success: 0,
172
+ consent: 0,
173
+ validationFailure: 0,
174
+ invalidJson: 0,
175
+ },
176
+ step: (ctx.state().analyze?.step ?? -1) + 1,
177
+ retry: attempt,
178
+ created_at: new Date().toISOString(),
179
+ };
180
+ } else {
181
+ throw e;
182
+ }
183
+ }
184
+
185
+ if (review.approved || attempt >= ANALYZE_SCENARIO_MAX_RETRY) {
186
+ analyzeDebug(
187
+ review.approved
188
+ ? `Scenario approved (attempt ${attempt})`
189
+ : `Scenario max retry reached (attempt ${attempt}), proceeding`,
190
+ );
191
+ scenario = rawScenario;
192
+ ctx.dispatch(scenario);
193
+ break;
194
+ }
195
+
196
+ analyzeDebug(`Scenario rejected (attempt ${attempt}): ${review.feedback}`);
197
+ scenarioFeedback = review.feedback;
198
+ }
199
+
200
+ // Initialize per-file state
201
+ const fileStates: IFileState[] = scenario.files.map((file) => ({
202
+ file,
203
+ moduleResult: null,
204
+ unitResults: null,
205
+ sectionResults: null,
206
+ }));
207
+
208
+ // Progress tracking for each stage
209
+ const moduleWriteProgress: AutoBeProgressEventBase = {
210
+ total: scenario.files.length,
211
+ completed: 0,
212
+ };
213
+ const unitWriteProgress: AutoBeProgressEventBase = {
214
+ total: 0,
215
+ completed: 0,
216
+ };
217
+ const sectionWriteProgress: AutoBeProgressEventBase = {
218
+ total: 0,
219
+ completed: 0,
220
+ };
221
+ const perFileSectionReviewProgress: AutoBeProgressEventBase = {
222
+ total: 0,
223
+ completed: 0,
224
+ };
225
+ const crossFileSectionReviewProgress: AutoBeProgressEventBase = {
226
+ total: 1,
227
+ completed: 0,
228
+ };
229
+
230
+ // === STAGE 1: MODULE (deterministic — no LLM) ===
231
+ processStageModuleDeterministic(ctx, {
232
+ scenario,
233
+ fileStates,
234
+ moduleWriteProgress,
235
+ });
236
+
237
+ // === STAGE 2: UNIT (fixed units deterministic, dynamic units LLM) ===
238
+ await processStageUnit(ctx, {
239
+ scenario,
240
+ fileStates,
241
+ unitWriteProgress,
242
+ });
243
+
244
+ // === STAGE 3: SECTION (01-05 only, TOC excluded) ===
245
+ await processStageSection(ctx, {
246
+ scenario,
247
+ fileStates,
248
+ sectionWriteProgress,
249
+ perFileSectionReviewProgress,
250
+ crossFileSectionReviewProgress,
251
+ });
252
+
253
+ // === TOC FILL (deterministic — no LLM) ===
254
+ const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
255
+ (scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
256
+ );
257
+ const tocIndex = fileStates.findIndex((s) => s.file.filename === "00-toc.md");
258
+ let tocContent: string | null = null;
259
+ if (tocIndex >= 0) {
260
+ tocContent = fillTocDeterministic(ctx, {
261
+ scenario,
262
+ tocFileState: fileStates[tocIndex]!,
263
+ otherFileStates: fileStates.filter((_, i) => i !== tocIndex),
264
+ expandedTemplate,
265
+ });
266
+ }
267
+
268
+ // === ASSEMBLE ===
269
+ const files: AutoBeAnalyze.IFile[] = [];
270
+ for (let fileIndex = 0; fileIndex < fileStates.length; fileIndex++) {
271
+ const state = fileStates[fileIndex]!;
272
+ // TOC uses flat content directly (no module/unit hierarchy)
273
+ const content =
274
+ fileIndex === tocIndex
275
+ ? tocContent!
276
+ : assembleContent(
277
+ state.moduleResult!,
278
+ state.unitResults!,
279
+ state.sectionResults!,
280
+ );
281
+ const module = assembleModule(
282
+ state.moduleResult!,
283
+ state.unitResults!,
284
+ state.sectionResults!,
285
+ );
286
+
287
+ files.push({
288
+ ...state.file,
289
+ title: state.moduleResult!.title,
290
+ summary: state.moduleResult!.summary,
291
+ content,
292
+ module,
293
+ });
294
+ }
295
+
296
+ // Complete the analysis
297
+ return ctx.dispatch({
298
+ type: "analyzeComplete",
299
+ id: v7(),
300
+ actors: scenario.actors,
301
+ prefix: scenario.prefix,
302
+ files,
303
+ aggregates: ctx.getCurrentAggregates("analyze"),
304
+ step,
305
+ elapsed: new Date().getTime() - startTime.getTime(),
306
+ created_at: new Date().toISOString(),
307
+ }) satisfies AutoBeAnalyzeHistory;
308
+ };
309
+
310
+ // MODULE (deterministic — no LLM calls)
311
+
312
+ /**
313
+ * Generate module structure deterministically from FixedAnalyzeTemplate.
314
+ *
315
+ * No LLM calls needed — module titles, purposes, and structure are all derived
316
+ * from the fixed 6-file SRS template.
317
+ */
318
+ function processStageModuleDeterministic(
319
+ ctx: AutoBeContext,
320
+ props: {
321
+ scenario: AutoBeAnalyzeScenarioEvent;
322
+ fileStates: IFileState[];
323
+ moduleWriteProgress: AutoBeProgressEventBase;
324
+ },
325
+ ): void {
326
+ const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
327
+ (props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
328
+ );
329
+ for (const [i, state] of props.fileStates.entries()) {
330
+ const template = expandedTemplate[i]!;
331
+ const moduleEvent: AutoBeAnalyzeWriteModuleEvent = {
332
+ type: "analyzeWriteModule",
333
+ id: v7(),
334
+ title: `${props.scenario.prefix} — ${template.description}`,
335
+ summary: template.description,
336
+ moduleSections: template.modules.map((m) => ({
337
+ title: m.title,
338
+ purpose: m.purpose,
339
+ content: m.purpose,
340
+ })),
341
+ step: (ctx.state().analyze?.step ?? -1) + 1,
342
+ retry: 0,
343
+ total: props.fileStates.length,
344
+ completed: i + 1,
345
+ tokenUsage: {
346
+ total: 0,
347
+ input: { total: 0, cached: 0 },
348
+ output: {
349
+ total: 0,
350
+ reasoning: 0,
351
+ accepted_prediction: 0,
352
+ rejected_prediction: 0,
353
+ },
354
+ },
355
+ metric: {
356
+ attempt: 0,
357
+ success: 0,
358
+ consent: 0,
359
+ validationFailure: 0,
360
+ invalidJson: 0,
361
+ },
362
+ acquisition: { previousAnalysisSections: [] },
363
+ created_at: new Date().toISOString(),
364
+ };
365
+ state.moduleResult = moduleEvent;
366
+ ctx.dispatch(moduleEvent);
367
+ props.moduleWriteProgress.completed++;
368
+ }
369
+ }
370
+
371
+ // UNIT
372
+
373
+ /**
374
+ * Process the Unit stage for all files.
375
+ *
376
+ * Fixed-strategy modules get deterministic unit generation (no LLM).
377
+ * Dynamic-strategy modules (perEntity/perActor/perEntityGroup) use LLM. No
378
+ * cross-file unit review — Hard Validators at section stage handle
379
+ * consistency.
380
+ */
381
+ async function processStageUnit(
382
+ ctx: AutoBeContext,
383
+ props: {
384
+ scenario: AutoBeAnalyzeScenarioEvent;
385
+ fileStates: IFileState[];
386
+ unitWriteProgress: AutoBeProgressEventBase;
387
+ },
388
+ ): Promise<void> {
389
+ const promptCacheKey: string = v7();
390
+ const expandedTemplate = buildFixedAnalyzeExpandedTemplate(
391
+ (props.scenario.features ?? []) as FixedAnalyzeTemplateFeature[],
392
+ );
393
+
394
+ // Count total units needed for progress tracking
395
+ for (const [fileIndex, state] of props.fileStates.entries()) {
396
+ const template = expandedTemplate[fileIndex]!;
397
+ props.unitWriteProgress.total += template.modules.length;
398
+ void state; // used below
399
+ }
400
+
401
+ await executeCachedBatch(
402
+ ctx,
403
+ props.fileStates.map((state, fileIndex) => async (cacheKey) => {
404
+ // TOC is filled deterministically after all other files complete
405
+ if (state.file.filename === "00-toc.md") return [];
406
+
407
+ const moduleResult: AutoBeAnalyzeWriteModuleEvent = state.moduleResult!;
408
+ const template = expandedTemplate[fileIndex]!;
409
+ analyzeDebug(
410
+ `unit file-start fileIndex=${fileIndex} file="${state.file.filename}"`,
411
+ );
412
+
413
+ const unitResults: AutoBeAnalyzeWriteUnitEvent[] = [];
414
+ for (
415
+ let moduleIndex: number = 0;
416
+ moduleIndex < moduleResult.moduleSections.length;
417
+ moduleIndex++
418
+ ) {
419
+ const moduleTemplate = template.modules[moduleIndex]!;
420
+ const strategy = moduleTemplate.unitStrategy;
421
+
422
+ if (strategy.type === "fixed") {
423
+ // Deterministic unit generation — no LLM
424
+ const unitEvent = buildDeterministicUnitEvent(ctx, {
425
+ moduleIndex,
426
+ units: strategy.units,
427
+ progress: props.unitWriteProgress,
428
+ });
429
+ ctx.dispatch(unitEvent);
430
+ unitResults.push(unitEvent);
431
+ } else {
432
+ // Dynamic units — expand from template, then LLM writes content+keywords
433
+ const unitStart: number = Date.now();
434
+ analyzeDebug(
435
+ `unit module-start fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} strategy=${strategy.type}`,
436
+ );
437
+ try {
438
+ const unitEvent: AutoBeAnalyzeWriteUnitEvent =
439
+ await orchestrateAnalyzeWriteUnit(ctx, {
440
+ scenario: props.scenario,
441
+ file: state.file,
442
+ moduleEvent: moduleResult,
443
+ moduleIndex,
444
+ progress: props.unitWriteProgress,
445
+ promptCacheKey: cacheKey,
446
+ retry: 0,
447
+ });
448
+ analyzeDebug(
449
+ `unit module-done fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitCount=${unitEvent.unitSections.length} elapsedMs=${Date.now() - unitStart}`,
450
+ );
451
+ unitResults.push(unitEvent);
452
+ } catch (e) {
453
+ if (
454
+ e instanceof AgenticaValidationError ||
455
+ e instanceof AutoBePreliminaryExhaustedError ||
456
+ e instanceof AutoBeTimeoutError
457
+ ) {
458
+ analyzeDebug(
459
+ `unit module-skipped fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} elapsedMs=${Date.now() - unitStart} — using fallback`,
460
+ );
461
+ const expandedUnits = expandFixedAnalyzeTemplateUnits(
462
+ moduleTemplate,
463
+ props.scenario.entities,
464
+ props.scenario.actors,
465
+ );
466
+ const fallbackEvent = buildDeterministicUnitEvent(ctx, {
467
+ moduleIndex,
468
+ units: expandedUnits,
469
+ progress: props.unitWriteProgress,
470
+ });
471
+ ctx.dispatch(fallbackEvent);
472
+ unitResults.push(fallbackEvent);
473
+ } else {
474
+ throw e;
475
+ }
476
+ }
477
+ }
478
+ }
479
+ state.unitResults = unitResults;
480
+ analyzeDebug(
481
+ `unit file-done fileIndex=${fileIndex} file="${state.file.filename}"`,
482
+ );
483
+ return unitResults;
484
+ }),
485
+ promptCacheKey,
486
+ );
487
+ }
488
+
489
+ /** Build a deterministic AutoBeAnalyzeWriteUnitEvent for fixed-strategy modules. */
490
+ function buildDeterministicUnitEvent(
491
+ ctx: AutoBeContext,
492
+ props: {
493
+ moduleIndex: number;
494
+ units: FixedAnalyzeTemplateUnitTemplate[];
495
+ progress: AutoBeProgressEventBase;
496
+ },
497
+ ): AutoBeAnalyzeWriteUnitEvent {
498
+ props.progress.completed++;
499
+ return {
500
+ type: "analyzeWriteUnit",
501
+ id: v7(),
502
+ moduleIndex: props.moduleIndex,
503
+ unitSections: props.units.map((u) => ({
504
+ title: u.titlePattern,
505
+ purpose: u.purposePattern,
506
+ content: u.purposePattern,
507
+ keywords: [...u.keywords],
508
+ })),
509
+ step: (ctx.state().analyze?.step ?? -1) + 1,
510
+ retry: 0,
511
+ total: props.progress.total,
512
+ completed: props.progress.completed,
513
+ tokenUsage: {
514
+ total: 0,
515
+ input: { total: 0, cached: 0 },
516
+ output: {
517
+ total: 0,
518
+ reasoning: 0,
519
+ accepted_prediction: 0,
520
+ rejected_prediction: 0,
521
+ },
522
+ },
523
+ metric: {
524
+ attempt: 0,
525
+ success: 0,
526
+ consent: 0,
527
+ validationFailure: 0,
528
+ invalidJson: 0,
529
+ },
530
+ acquisition: { previousAnalysisSections: [] },
531
+ created_at: new Date().toISOString(),
532
+ };
533
+ }
534
+
535
+ // SECTION
536
+
537
+ /**
538
+ * Process the Section stage for all files with 2-pass review.
539
+ *
540
+ * Flow:
541
+ *
542
+ * 1. Write sections for pending files in parallel
543
+ * 2. Pass 1: Per-file detailed review (parallel) — validates EARS format, value
544
+ * consistency, bridge blocks, intra-file deduplication
545
+ * 3. Pass 2: Cross-file lightweight review (single call) — validates terminology
546
+ * alignment, value consistency across files, naming conventions
547
+ * 4. Merge results from both passes — reject if either pass rejects
548
+ * 5. Retry only rejected files (max 3 attempts)
549
+ */
550
+ async function processStageSection(
551
+ ctx: AutoBeContext,
552
+ props: {
553
+ scenario: AutoBeAnalyzeScenarioEvent;
554
+ fileStates: IFileState[];
555
+ sectionWriteProgress: AutoBeProgressEventBase;
556
+ perFileSectionReviewProgress: AutoBeProgressEventBase;
557
+ crossFileSectionReviewProgress: AutoBeProgressEventBase;
558
+ },
559
+ ): Promise<void> {
560
+ // Exclude TOC (00-toc.md) — it is filled deterministically after all files
561
+ const pendingIndices: Set<number> = new Set(
562
+ props.fileStates
563
+ .map((s, i) => (s.file.filename === "00-toc.md" ? -1 : i))
564
+ .filter((i) => i >= 0),
565
+ );
566
+ let crossFileReviewCount: number = 0;
567
+
568
+ for (
569
+ let attempt: number = 0;
570
+ attempt < AutoBeConfigConstant.ANALYZE_RETRY && pendingIndices.size > 0;
571
+ attempt++
572
+ ) {
573
+ // Dynamically increase progress for retries (module-level granularity)
574
+ const pendingModuleCount = [...pendingIndices].reduce(
575
+ (sum, fi) => sum + (props.fileStates[fi]?.unitResults?.length ?? 1),
576
+ 0,
577
+ );
578
+ props.perFileSectionReviewProgress.total += pendingModuleCount;
579
+ if (attempt > 0) {
580
+ props.crossFileSectionReviewProgress.total++;
581
+ }
582
+
583
+ // Write sections for pending files in parallel
584
+ const pendingArray: number[] = [...pendingIndices];
585
+ const sectionFileBatches: number[][] = chunkSectionFileIndices(
586
+ pendingArray,
587
+ computeSectionBatchSize({
588
+ attempt,
589
+ pendingCount: pendingArray.length,
590
+ }),
591
+ );
592
+ const promptCacheKey: string = v7();
593
+
594
+ // Build scenario entity name list for invention validation (P0-B)
595
+ const scenarioEntityNames = props.scenario.entities.map((e) => e.name);
596
+
597
+ // Collect per-file review results (populated inside write+review batch)
598
+ const perFileReviewResults: Map<number, AutoBeAnalyzeSectionReviewEvent> =
599
+ new Map();
600
+
601
+ for (const sectionBatch of sectionFileBatches)
602
+ await executeCachedBatch(
603
+ ctx,
604
+ sectionBatch.map((fileIndex) => async (cacheKey) => {
605
+ const state: IFileState = props.fileStates[fileIndex]!;
606
+ const moduleResult: AutoBeAnalyzeWriteModuleEvent =
607
+ state.moduleResult!;
608
+ const unitResults: AutoBeAnalyzeWriteUnitEvent[] = state.unitResults!;
609
+ analyzeDebug(
610
+ `section file-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" batchSize=${sectionBatch.length}`,
611
+ );
612
+
613
+ // Build rejected module/unit lookup for selective regeneration
614
+ const rejectedSet: Set<string> | null = buildRejectedSet(
615
+ state.rejectedModuleUnits,
616
+ );
617
+ const feedbackMap: Map<string, ISectionAwareFeedback> =
618
+ buildFeedbackMap(state.rejectedModuleUnits);
619
+
620
+ // Increase write progress only for sections that will be regenerated
621
+ for (let mi: number = 0; mi < unitResults.length; mi++) {
622
+ const unitEvent: AutoBeAnalyzeWriteUnitEvent = unitResults[mi]!;
623
+ for (let ui: number = 0; ui < unitEvent.unitSections.length; ui++) {
624
+ if (isSectionRejected(rejectedSet, mi, ui)) {
625
+ props.sectionWriteProgress.total++;
626
+ }
627
+ }
628
+ }
629
+
630
+ // Write sections, skipping approved ones on retry
631
+ const sectionResults: AutoBeAnalyzeWriteSectionEvent[][] = [];
632
+ for (
633
+ let moduleIndex: number = 0;
634
+ moduleIndex < unitResults.length;
635
+ moduleIndex++
636
+ ) {
637
+ const unitEvent: AutoBeAnalyzeWriteUnitEvent =
638
+ unitResults[moduleIndex]!;
639
+ const sectionsForModule: AutoBeAnalyzeWriteSectionEvent[] = [];
640
+
641
+ for (
642
+ let unitIndex: number = 0;
643
+ unitIndex < unitEvent.unitSections.length;
644
+ unitIndex++
645
+ ) {
646
+ if (isSectionRejected(rejectedSet, moduleIndex, unitIndex)) {
647
+ const sectionStart: number = Date.now();
648
+ // Regenerate this section with targeted feedback
649
+ const targetedInfo: ISectionAwareFeedback | undefined =
650
+ feedbackMap.get(`${moduleIndex}:${unitIndex}`);
651
+ const targetedFeedback: string | undefined =
652
+ targetedInfo?.feedback ?? state.sectionFeedback;
653
+ const targetedSectionIndices: number[] | null =
654
+ targetedInfo?.sectionIndices ?? null;
655
+ analyzeDebug(
656
+ `section unit-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} targetSections=${targetedSectionIndices ? `[${targetedSectionIndices.join(",")}]` : "all"}`,
657
+ );
658
+ const previousSection:
659
+ | AutoBeAnalyzeWriteSectionEvent
660
+ | undefined =
661
+ state.sectionResults?.[moduleIndex]?.[unitIndex];
662
+ let sectionEvent: AutoBeAnalyzeWriteSectionEvent;
663
+ try {
664
+ sectionEvent =
665
+ previousSection && targetedFeedback?.trim()
666
+ ? await orchestrateAnalyzeWriteSectionPatch(ctx, {
667
+ scenario: props.scenario,
668
+ file: state.file,
669
+ moduleEvent: moduleResult,
670
+ unitEvent,
671
+ moduleIndex,
672
+ unitIndex,
673
+ previousSectionEvent: previousSection,
674
+ feedback: targetedFeedback,
675
+ progress: props.sectionWriteProgress,
676
+ promptCacheKey: cacheKey,
677
+ retry: attempt,
678
+ scenarioEntityNames,
679
+ sectionIndices: targetedSectionIndices,
680
+ })
681
+ : await orchestrateAnalyzeWriteSection(ctx, {
682
+ scenario: props.scenario,
683
+ file: state.file,
684
+ moduleEvent: moduleResult,
685
+ unitEvent,
686
+ allUnitEvents: unitResults,
687
+ moduleIndex,
688
+ unitIndex,
689
+ progress: props.sectionWriteProgress,
690
+ promptCacheKey: cacheKey,
691
+ feedback: targetedFeedback,
692
+ retry: attempt,
693
+ scenarioEntityNames,
694
+ });
695
+ } catch (e) {
696
+ if (
697
+ e instanceof AgenticaValidationError ||
698
+ e instanceof AutoBePreliminaryExhaustedError ||
699
+ e instanceof AutoBeTimeoutError
700
+ ) {
701
+ analyzeDebug(
702
+ `section unit-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} error=${(e as Error).constructor.name} — ${previousSection ? "reusing previous" : "using placeholder"}`,
703
+ );
704
+ if (previousSection) {
705
+ sectionEvent = previousSection;
706
+ } else {
707
+ sectionEvent = {
708
+ type: "analyzeWriteSection",
709
+ id: v7(),
710
+ moduleIndex,
711
+ unitIndex,
712
+ sectionSections: [],
713
+ acquisition: { previousAnalysisSections: [] },
714
+ tokenUsage: {
715
+ total: 0,
716
+ input: { total: 0, cached: 0 },
717
+ output: {
718
+ total: 0,
719
+ reasoning: 0,
720
+ accepted_prediction: 0,
721
+ rejected_prediction: 0,
722
+ },
723
+ },
724
+ metric: {
725
+ attempt: 0,
726
+ success: 0,
727
+ consent: 0,
728
+ validationFailure: 0,
729
+ invalidJson: 0,
730
+ },
731
+ step: (ctx.state().analyze?.step ?? -1) + 1,
732
+ total: props.sectionWriteProgress.total,
733
+ completed: ++props.sectionWriteProgress.completed,
734
+ retry: attempt,
735
+ created_at: new Date().toISOString(),
736
+ };
737
+ ctx.dispatch(sectionEvent);
738
+ }
739
+ } else {
740
+ throw e;
741
+ }
742
+ }
743
+ analyzeDebug(
744
+ `section unit-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} unitIndex=${unitIndex} sectionCount=${sectionEvent.sectionSections.length} elapsedMs=${Date.now() - sectionStart}`,
745
+ );
746
+ sectionsForModule.push(sectionEvent);
747
+ } else {
748
+ // Keep existing approved section
749
+ sectionsForModule.push(
750
+ state.sectionResults![moduleIndex]![unitIndex]!,
751
+ );
752
+ }
753
+ }
754
+ sectionResults.push(sectionsForModule);
755
+ }
756
+ state.sectionResults = sectionResults;
757
+
758
+ analyzeDebug(
759
+ `section file-write-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}"`,
760
+ );
761
+
762
+ // Per-module review immediately after write (removes barrier)
763
+ const reviewStart: number = Date.now();
764
+ analyzeDebug(
765
+ `section per-module-review-start attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${state.unitResults!.length}`,
766
+ );
767
+ const moduleReviews: AutoBeAnalyzeSectionReviewEvent[] = [];
768
+ for (
769
+ let moduleIndex = 0;
770
+ moduleIndex < state.unitResults!.length;
771
+ moduleIndex++
772
+ ) {
773
+ try {
774
+ const moduleReviewEvent = await orchestrateAnalyzeSectionReview(
775
+ ctx,
776
+ {
777
+ scenario: props.scenario,
778
+ fileIndex,
779
+ file: state.file,
780
+ moduleEvent: state.moduleResult!,
781
+ moduleIndex,
782
+ unitEvent: state.unitResults![moduleIndex]!,
783
+ moduleSectionEvents: state.sectionResults![moduleIndex]!,
784
+ siblingModuleSummaries: buildSiblingModuleSummaries(
785
+ state.moduleResult!,
786
+ state.sectionResults!,
787
+ moduleIndex,
788
+ ),
789
+ feedback: state.sectionFeedback,
790
+ progress: props.perFileSectionReviewProgress,
791
+ promptCacheKey: cacheKey,
792
+ retry: attempt,
793
+ },
794
+ );
795
+ moduleReviews.push(moduleReviewEvent);
796
+ } catch (e) {
797
+ if (
798
+ e instanceof AgenticaValidationError ||
799
+ e instanceof AutoBeTimeoutError ||
800
+ e instanceof AutoBePreliminaryExhaustedError
801
+ ) {
802
+ analyzeDebug(
803
+ `section per-module-review-force-pass attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" moduleIndex=${moduleIndex} error=${(e as Error).constructor.name} — skipping this module review`,
804
+ );
805
+ } else {
806
+ throw e;
807
+ }
808
+ }
809
+ }
810
+ const reviewEvent = mergeModuleReviewEvents(moduleReviews, fileIndex);
811
+ analyzeDebug(
812
+ `section per-module-review-done attempt=${attempt} fileIndex=${fileIndex} file="${state.file.filename}" modules=${moduleReviews.length} elapsedMs=${Date.now() - reviewStart}`,
813
+ );
814
+ perFileReviewResults.set(fileIndex, reviewEvent!);
815
+
816
+ return sectionResults;
817
+ }),
818
+ promptCacheKey,
819
+ );
820
+
821
+ // Pass 2: Cross-file lightweight review (single call)
822
+ crossFileReviewCount++;
823
+ if (crossFileReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
824
+ analyzeDebug(
825
+ `[orchestrateAnalyze] Section stage: skipping cross-file review (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded)`,
826
+ );
827
+ // Force-pass all pending files
828
+ for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
829
+ break;
830
+ }
831
+ analyzeDebug(`section cross-file-validation-start attempt=${attempt}`);
832
+ const filesWithSections = props.fileStates
833
+ .filter((state) => state.sectionResults !== null)
834
+ .map((state) => ({
835
+ file: state.file,
836
+ sectionEvents: state.sectionResults!,
837
+ }));
838
+
839
+ // Pass 2a: Programmatic cross-file validation (BEFORE LLM review)
840
+ const criticalConflicts = detectConstraintConflicts({
841
+ files: filesWithSections,
842
+ });
843
+ const fileConflictMap: Map<string, string[]> =
844
+ buildFileConflictMap(criticalConflicts);
845
+
846
+ const attributeDuplicates = detectAttributeDuplicates({
847
+ files: filesWithSections,
848
+ });
849
+ const fileAttributeDuplicateMap: Map<string, string[]> =
850
+ buildFileAttributeDuplicateMap(attributeDuplicates);
851
+
852
+ const enumConflicts = detectEnumConflicts({
853
+ files: filesWithSections,
854
+ });
855
+ const fileEnumConflictMap: Map<string, string[]> =
856
+ buildFileEnumConflictMap(enumConflicts);
857
+
858
+ const permissionConflicts = detectPermissionConflicts({
859
+ files: filesWithSections,
860
+ });
861
+ const filePermissionConflictMap: Map<string, string[]> =
862
+ buildFilePermissionConflictMap(permissionConflicts);
863
+
864
+ const stateFieldConflicts = detectStateFieldConflicts({
865
+ files: filesWithSections,
866
+ });
867
+ const fileStateFieldConflictMap: Map<string, string[]> =
868
+ buildFileStateFieldConflictMap(stateFieldConflicts);
869
+
870
+ const errorCodeConflicts = detectErrorCodeConflicts({
871
+ files: filesWithSections,
872
+ });
873
+ const fileErrorCodeConflictMap: Map<string, string[]> =
874
+ buildFileErrorCodeConflictMap(errorCodeConflicts);
875
+
876
+ const proseConflicts = detectProseConstraintConflicts({
877
+ files: filesWithSections,
878
+ });
879
+ const fileProseConflictMap: Map<string, string[]> =
880
+ buildFileProseConflictMap(proseConflicts);
881
+
882
+ const oversizedTocMap: Map<number, string[]> = new Map();
883
+ for (const fileIndex of pendingArray) {
884
+ const state = props.fileStates[fileIndex]!;
885
+ if (state.file.filename === "00-toc.md" && state.sectionResults) {
886
+ const violations = detectOversizedToc(state.sectionResults);
887
+ if (violations.length > 0) {
888
+ oversizedTocMap.set(fileIndex, violations);
889
+ }
890
+ }
891
+ }
892
+
893
+ // Build mechanical violation summary for LLM context
894
+ const allMechanicalViolations: string[] = [
895
+ ...criticalConflicts.map(
896
+ (c) =>
897
+ `Constraint conflict: ${c.key} — ${c.values.map((v) => `"${v.display}" in [${v.files.join(", ")}]`).join(" vs ")}`,
898
+ ),
899
+ ...attributeDuplicates.map(
900
+ (d) => `Attribute duplication: ${d.key} in [${d.files.join(", ")}]`,
901
+ ),
902
+ ...enumConflicts.map(
903
+ (c) =>
904
+ `Enum conflict: ${c.key} — ${c.values.map((v) => `enum(${v.enumSet}) in [${v.files.join(", ")}]`).join(" vs ")}`,
905
+ ),
906
+ ...errorCodeConflicts.map(
907
+ (c) =>
908
+ `Error code conflict: ${c.conditionKey} — ${c.codes.map((cd) => `HTTP ${cd.httpStatus} in [${cd.files.join(", ")}]`).join(" vs ")}`,
909
+ ),
910
+ ...proseConflicts.map(
911
+ (c) =>
912
+ `Prose constraint conflict: ${c.entityAttr} — canonical [${c.canonicalValues.join(", ")}] vs prose [${c.proseValues.join(", ")}] in ${c.file}`,
913
+ ),
914
+ ];
915
+ const mechanicalViolationSummary =
916
+ allMechanicalViolations.length > 0
917
+ ? allMechanicalViolations.join("\n")
918
+ : undefined;
919
+
920
+ // Pass 2b: Cross-file semantic LLM review (with mechanical violations excluded)
921
+ analyzeDebug(`section cross-file-review-start attempt=${attempt}`);
922
+ let crossFileReviewEvent: AutoBeAnalyzeSectionReviewEvent | null = null;
923
+ try {
924
+ crossFileReviewEvent = await orchestrateAnalyzeSectionCrossFileReview(
925
+ ctx,
926
+ {
927
+ scenario: props.scenario,
928
+ allFileSummaries: props.fileStates
929
+ .filter((s) => s.file.filename !== "00-toc.md")
930
+ .map((state) => {
931
+ const fi = props.fileStates.indexOf(state);
932
+ return {
933
+ file: state.file,
934
+ moduleEvent: state.moduleResult!,
935
+ unitEvents: state.unitResults!,
936
+ sectionEvents: state.sectionResults!,
937
+ status: pendingIndices.has(fi)
938
+ ? attempt === 0
939
+ ? ("new" as const)
940
+ : ("rewritten" as const)
941
+ : ("approved" as const),
942
+ };
943
+ }),
944
+ mechanicalViolationSummary,
945
+ progress: props.crossFileSectionReviewProgress,
946
+ promptCacheKey,
947
+ retry: attempt,
948
+ },
949
+ );
950
+ } catch (e) {
951
+ if (
952
+ e instanceof AgenticaValidationError ||
953
+ e instanceof AutoBeTimeoutError ||
954
+ e instanceof AutoBePreliminaryExhaustedError
955
+ ) {
956
+ analyzeDebug(
957
+ `section cross-file-review-force-pass attempt=${attempt} error=${(e as Error).constructor.name} — force-passing all pending files`,
958
+ );
959
+ for (const fileIndex of pendingArray) pendingIndices.delete(fileIndex);
960
+ break;
961
+ }
962
+ throw e;
963
+ }
964
+ analyzeDebug(
965
+ `section cross-file-review-done attempt=${attempt} results=${crossFileReviewEvent.fileResults.length}`,
966
+ );
967
+
968
+ // Merge results from both passes
969
+ const crossFileResultMap: Map<
970
+ number,
971
+ AutoBeAnalyzeSectionReviewFileResult
972
+ > = new Map();
973
+ const validCrossFileResults = filterValidFileResults(
974
+ crossFileReviewEvent.fileResults,
975
+ props.fileStates.length,
976
+ "Section cross-file review",
977
+ );
978
+ for (const fr of validCrossFileResults)
979
+ crossFileResultMap.set(fr.fileIndex, fr);
980
+
981
+ for (const fileIndex of pendingArray) {
982
+ const state: IFileState = props.fileStates[fileIndex]!;
983
+
984
+ // Increment review count and force-pass if exceeded limit
985
+ state.sectionReviewCount = (state.sectionReviewCount ?? 0) + 1;
986
+ if (state.sectionReviewCount > ANALYZE_SECTION_FILE_MAX_REVIEW) {
987
+ analyzeDebug(
988
+ `[orchestrateAnalyze] Section stage: force-passing (max review ${ANALYZE_SECTION_FILE_MAX_REVIEW} exceeded) for file "${state.file.filename}"`,
989
+ );
990
+ pendingIndices.delete(fileIndex);
991
+ continue;
992
+ }
993
+
994
+ const perFileEvent = perFileReviewResults.get(fileIndex);
995
+ const perFileResult = perFileEvent?.fileResults[0];
996
+ const crossFileResult = crossFileResultMap.get(fileIndex);
997
+
998
+ const perFileApproved = perFileResult?.approved ?? true;
999
+ const crossFileApproved = crossFileResult?.approved ?? true;
1000
+
1001
+ // Check if this file has programmatically-detected critical conflicts
1002
+ const filename = state.file.filename;
1003
+ const fileCriticalConflicts = fileConflictMap.get(filename) ?? [];
1004
+ const fileAttrDuplicates = fileAttributeDuplicateMap.get(filename) ?? [];
1005
+ const fileEnumConflicts = fileEnumConflictMap.get(filename) ?? [];
1006
+ const filePermissionConflicts =
1007
+ filePermissionConflictMap.get(filename) ?? [];
1008
+ const fileStateFieldConflicts =
1009
+ fileStateFieldConflictMap.get(filename) ?? [];
1010
+ const fileErrorCodeConflicts =
1011
+ fileErrorCodeConflictMap.get(filename) ?? [];
1012
+ const fileOversizedToc = oversizedTocMap.get(fileIndex) ?? [];
1013
+ const fileProseConflicts = fileProseConflictMap.get(filename) ?? [];
1014
+ const hasCriticalConflict =
1015
+ fileCriticalConflicts.length > 0 ||
1016
+ fileAttrDuplicates.length > 0 ||
1017
+ fileEnumConflicts.length > 0 ||
1018
+ filePermissionConflicts.length > 0 ||
1019
+ fileStateFieldConflicts.length > 0 ||
1020
+ fileErrorCodeConflicts.length > 0 ||
1021
+ fileOversizedToc.length > 0 ||
1022
+ fileProseConflicts.length > 0;
1023
+
1024
+ // Decision logic:
1025
+ // 1. per-file reject → reject (unchanged)
1026
+ // 2. per-file approve + critical conflict detected → reject (NEW: patch-first)
1027
+ // 3. per-file approve + no critical conflict → approve (unchanged)
1028
+ const approved = perFileApproved && !hasCriticalConflict;
1029
+
1030
+ const structuredPerFileIssues =
1031
+ collectStructuredReviewIssues(perFileResult);
1032
+ const structuredCrossFileIssues =
1033
+ collectStructuredReviewIssues(crossFileResult);
1034
+ const programmaticIssues = buildProgrammaticSectionIssues({
1035
+ fileCriticalConflicts,
1036
+ fileAttrDuplicates,
1037
+ fileEnumConflicts,
1038
+ filePermissionConflicts,
1039
+ fileStateFieldConflicts,
1040
+ fileErrorCodeConflicts,
1041
+ fileOversizedToc,
1042
+ fileProseConflicts,
1043
+ });
1044
+
1045
+ if (approved) {
1046
+ // NOTE: revisedSections intentionally ignored — approved means pass as-is.
1047
+ // Applying revisedSections caused infinite re-write loops (sections kept growing).
1048
+ // Pass cross-file feedback as advisory for next retry's context
1049
+ if (!crossFileApproved && crossFileResult?.feedback) {
1050
+ state.sectionFeedback = `[Cross-file advisory] ${crossFileResult.feedback}`;
1051
+ }
1052
+ state.sectionRetryCount = 0;
1053
+ state.sectionStagnationCount = 0;
1054
+ state.lastSectionContentSignature = undefined;
1055
+ state.lastSectionRejectionSignature = undefined;
1056
+ pendingIndices.delete(fileIndex);
1057
+ } else if (!perFileApproved) {
1058
+ // Per-file rejected: store only the latest per-file feedback (no accumulation)
1059
+ state.sectionFeedback = formatStructuredIssuesForRetry({
1060
+ fallbackFeedback: perFileResult?.feedback ?? "",
1061
+ issues: structuredPerFileIssues,
1062
+ });
1063
+
1064
+ // Use only per-file rejectedModuleUnits (no cross-file merge)
1065
+ state.rejectedModuleUnits = normalizeRejectedModuleUnits(
1066
+ perFileResult?.rejectedModuleUnits ?? null,
1067
+ structuredPerFileIssues,
1068
+ );
1069
+ // Fallback: infer targets from issues to avoid full-file rewrite
1070
+ if (state.rejectedModuleUnits === null) {
1071
+ state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
1072
+ structuredPerFileIssues,
1073
+ state.unitResults!,
1074
+ );
1075
+ }
1076
+ analyzeDebug(
1077
+ `section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
1078
+ state.rejectedModuleUnits,
1079
+ )} issues=${formatReviewIssuesSummary(structuredPerFileIssues)} feedback=${truncateForDebug(
1080
+ state.sectionFeedback ?? "",
1081
+ 500,
1082
+ )}`,
1083
+ );
1084
+ } else {
1085
+ // Critical conflict rejected (per-file approved but programmatic violations exist)
1086
+ // Use cross-file rejectedModuleUnits for targeted patch if available
1087
+ state.sectionFeedback = formatStructuredIssuesForRetry({
1088
+ fallbackFeedback:
1089
+ `[Critical conflict] ${[
1090
+ ...fileCriticalConflicts,
1091
+ ...fileAttrDuplicates,
1092
+ ...fileEnumConflicts,
1093
+ ...fileProseConflicts,
1094
+ ].join("; ")}` +
1095
+ (crossFileResult?.feedback ? `\n${crossFileResult.feedback}` : ""),
1096
+ issues: [...programmaticIssues, ...structuredCrossFileIssues],
1097
+ });
1098
+ state.rejectedModuleUnits = normalizeRejectedModuleUnits(
1099
+ crossFileResult?.rejectedModuleUnits ?? null,
1100
+ [...programmaticIssues, ...structuredCrossFileIssues],
1101
+ );
1102
+ // Fallback: infer targets from issues to avoid full-file rewrite
1103
+ if (state.rejectedModuleUnits === null) {
1104
+ state.rejectedModuleUnits = inferRejectedModuleUnitsFromIssues(
1105
+ [...programmaticIssues, ...structuredCrossFileIssues],
1106
+ state.unitResults!,
1107
+ );
1108
+ }
1109
+ analyzeDebug(
1110
+ `section reject file="${state.file.filename}" attempt=${attempt} perFileApproved=${perFileApproved} crossFileApproved=${crossFileApproved} critical=${hasCriticalConflict} targets=${formatRejectedModuleUnitsSummary(
1111
+ state.rejectedModuleUnits,
1112
+ )} issues=${formatReviewIssuesSummary([
1113
+ ...programmaticIssues,
1114
+ ...structuredCrossFileIssues,
1115
+ ])} feedback=${truncateForDebug(state.sectionFeedback ?? "", 500)}`,
1116
+ );
1117
+ }
1118
+
1119
+ if (!approved) {
1120
+ const contentSignature = buildSectionContentSignature(state);
1121
+ const rejectionSignature = buildSectionRejectionSignature({
1122
+ rejectedModuleUnits: state.rejectedModuleUnits ?? null,
1123
+ feedback: state.sectionFeedback ?? "",
1124
+ });
1125
+ const isStagnant =
1126
+ state.lastSectionContentSignature === contentSignature &&
1127
+ state.lastSectionRejectionSignature === rejectionSignature;
1128
+ state.sectionStagnationCount = isStagnant
1129
+ ? (state.sectionStagnationCount ?? 0) + 1
1130
+ : 0;
1131
+ state.sectionRetryCount = (state.sectionRetryCount ?? 0) + 1;
1132
+ state.lastSectionContentSignature = contentSignature;
1133
+ state.lastSectionRejectionSignature = rejectionSignature;
1134
+
1135
+ if ((state.sectionRetryCount ?? 0) > ANALYZE_SECTION_FILE_MAX_RETRY) {
1136
+ analyzeDebug(
1137
+ `[orchestrateAnalyze] Section stage: force-passing (max retry exceeded: ${ANALYZE_SECTION_FILE_MAX_RETRY}) for file "${state.file.filename}"`,
1138
+ );
1139
+ pendingIndices.delete(fileIndex);
1140
+ continue;
1141
+ }
1142
+ if (
1143
+ (state.sectionStagnationCount ?? 0) >= ANALYZE_SECTION_STAGNATION_MAX
1144
+ ) {
1145
+ analyzeDebug(
1146
+ `[orchestrateAnalyze] Section stage: force-passing (stagnation detected ${state.sectionStagnationCount}x) for file "${state.file.filename}"`,
1147
+ );
1148
+ pendingIndices.delete(fileIndex);
1149
+ continue;
1150
+ }
1151
+ }
1152
+ }
1153
+ }
1154
+
1155
+ if (pendingIndices.size > 0) {
1156
+ analyzeDebug(
1157
+ `[orchestrateAnalyze] Section stage: force-passing after max retries for files: ${[
1158
+ ...pendingIndices,
1159
+ ]
1160
+ .map((i) => props.fileStates[i]!.file.filename)
1161
+ .join(", ")}`,
1162
+ );
1163
+ }
1164
+ }
1165
+
1166
+ // ─── Section-stage helper functions ───
1167
+
1168
+ function computeSectionBatchSize(props: {
1169
+ attempt: number;
1170
+ pendingCount: number;
1171
+ }): number {
1172
+ return Math.min(8, props.pendingCount);
1173
+ }
1174
+
1175
+ function chunkSectionFileIndices(indices: number[], size: number): number[][] {
1176
+ if (indices.length === 0) return [];
1177
+ if (size <= 0 || size >= indices.length) return [indices];
1178
+ const chunks: number[][] = [];
1179
+ for (let i = 0; i < indices.length; i += size)
1180
+ chunks.push(indices.slice(i, i + size));
1181
+ return chunks;
1182
+ }
1183
+
1184
+ function buildRejectedSet(
1185
+ rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1186
+ ): Set<string> | null {
1187
+ if (rejected == null) return null;
1188
+ if (rejected.length === 0) return null;
1189
+ const set: Set<string> = new Set();
1190
+ for (const entry of rejected) {
1191
+ for (const ui of entry.unitIndices) {
1192
+ set.add(`${entry.moduleIndex}:${ui}`);
1193
+ }
1194
+ }
1195
+ return set.size > 0 ? set : null;
1196
+ }
1197
+
1198
+ interface ISectionAwareFeedback {
1199
+ feedback: string;
1200
+ sectionIndices: number[] | null;
1201
+ }
1202
+
1203
+ function buildFeedbackMap(
1204
+ rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1205
+ ): Map<string, ISectionAwareFeedback> {
1206
+ const map: Map<string, ISectionAwareFeedback> = new Map();
1207
+ if (rejected == null) return map;
1208
+ for (const entry of rejected) {
1209
+ for (const ui of entry.unitIndices) {
1210
+ map.set(`${entry.moduleIndex}:${ui}`, {
1211
+ feedback: formatRejectedModuleUnitFeedback(entry, ui),
1212
+ sectionIndices: entry.sectionIndicesPerUnit?.[ui] ?? null,
1213
+ });
1214
+ }
1215
+ }
1216
+ return map;
1217
+ }
1218
+
1219
+ function isSectionRejected(
1220
+ rejectedSet: Set<string> | null,
1221
+ moduleIndex: number,
1222
+ unitIndex: number,
1223
+ ): boolean {
1224
+ if (rejectedSet === null) return true;
1225
+ return rejectedSet.has(`${moduleIndex}:${unitIndex}`);
1226
+ }
1227
+
1228
+ function filterValidFileResults<T extends { fileIndex: number }>(
1229
+ fileResults: T[],
1230
+ fileCount: number,
1231
+ stage: string,
1232
+ ): T[] {
1233
+ return fileResults.filter((fr) => {
1234
+ if (
1235
+ Number.isInteger(fr.fileIndex) &&
1236
+ fr.fileIndex >= 0 &&
1237
+ fr.fileIndex < fileCount
1238
+ ) {
1239
+ return true;
1240
+ }
1241
+ console.warn(
1242
+ `[orchestrateAnalyze] ${stage}: invalid fileIndex ${fr.fileIndex} (valid: 0-${fileCount - 1})`,
1243
+ );
1244
+ return false;
1245
+ });
1246
+ }
1247
+
1248
+ function formatRejectedModuleUnitFeedback(
1249
+ entry: AutoBeAnalyzeSectionReviewRejectedModuleUnit,
1250
+ unitIndex: number,
1251
+ ): string {
1252
+ const scopedIssues = (entry.issues ?? []).filter(
1253
+ (issue) =>
1254
+ issue.moduleIndex === entry.moduleIndex &&
1255
+ (issue.unitIndex === null || issue.unitIndex === unitIndex),
1256
+ );
1257
+ if (scopedIssues.length === 0) return entry.feedback;
1258
+ return [
1259
+ entry.feedback,
1260
+ ...scopedIssues.map(
1261
+ (issue) =>
1262
+ `- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}`,
1263
+ ),
1264
+ ].join("\n");
1265
+ }
1266
+
1267
+ function collectStructuredReviewIssues(
1268
+ result:
1269
+ | {
1270
+ feedback: string;
1271
+ rejectedModuleUnits?:
1272
+ | AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
1273
+ | null;
1274
+ issues?: AutoBeAnalyzeSectionReviewIssue[] | null;
1275
+ }
1276
+ | undefined,
1277
+ ): AutoBeAnalyzeSectionReviewIssue[] {
1278
+ if (!result) return [];
1279
+ const collected: AutoBeAnalyzeSectionReviewIssue[] = [];
1280
+
1281
+ for (const issue of result.issues ?? []) collected.push(issue);
1282
+ for (const group of result.rejectedModuleUnits ?? []) {
1283
+ for (const issue of group.issues ?? []) collected.push(issue);
1284
+ if ((group.issues?.length ?? 0) === 0) {
1285
+ for (const unitIndex of group.unitIndices) {
1286
+ collected.push({
1287
+ ruleCode: "section_review_reject",
1288
+ moduleIndex: group.moduleIndex,
1289
+ unitIndex,
1290
+ fixInstruction:
1291
+ group.feedback || result.feedback || "Fix review issues.",
1292
+ evidence: null,
1293
+ });
1294
+ }
1295
+ }
1296
+ }
1297
+
1298
+ if (collected.length === 0 && result.feedback.trim().length > 0) {
1299
+ collected.push({
1300
+ ruleCode: "section_review_reject",
1301
+ moduleIndex: null,
1302
+ unitIndex: null,
1303
+ fixInstruction: result.feedback,
1304
+ evidence: null,
1305
+ });
1306
+ }
1307
+ return dedupeReviewIssues(collected);
1308
+ }
1309
+
1310
+ function buildProgrammaticSectionIssues(props: {
1311
+ fileCriticalConflicts: string[];
1312
+ fileAttrDuplicates: string[];
1313
+ fileEnumConflicts: string[];
1314
+ filePermissionConflicts: string[];
1315
+ fileStateFieldConflicts: string[];
1316
+ fileErrorCodeConflicts: string[];
1317
+ fileOversizedToc: string[];
1318
+ fileProseConflicts: string[];
1319
+ }): AutoBeAnalyzeSectionReviewIssue[] {
1320
+ return [
1321
+ ...props.fileCriticalConflicts.map((detail) => ({
1322
+ ruleCode: "cross_file_constraint_conflict",
1323
+ moduleIndex: null,
1324
+ unitIndex: null,
1325
+ fixInstruction:
1326
+ "Align conflicting constraints/values with other files and preserve one canonical value.",
1327
+ evidence: detail,
1328
+ })),
1329
+ ...props.fileAttrDuplicates.map((detail) => ({
1330
+ ruleCode: "cross_file_attribute_duplicate",
1331
+ moduleIndex: null,
1332
+ unitIndex: null,
1333
+ fixInstruction:
1334
+ "Remove duplicate attribute specifications across files and keep ownership in one file.",
1335
+ evidence: detail,
1336
+ })),
1337
+ ...props.fileEnumConflicts.map((detail) => ({
1338
+ ruleCode: "cross_file_enum_conflict",
1339
+ moduleIndex: null,
1340
+ unitIndex: null,
1341
+ fixInstruction:
1342
+ "Align enum values with the canonical definition from the first file that specified this attribute. Use the exact same enum set.",
1343
+ evidence: detail,
1344
+ })),
1345
+ ...props.filePermissionConflicts.map((detail) => ({
1346
+ ruleCode: "cross_file_permission_conflict",
1347
+ moduleIndex: null,
1348
+ unitIndex: null,
1349
+ fixInstruction:
1350
+ "Align permission rules with the canonical definition. If the first file says 'denied', all files must say 'denied' for the same actor→operation.",
1351
+ evidence: detail,
1352
+ })),
1353
+ ...props.fileStateFieldConflicts.map((detail) => ({
1354
+ ruleCode: "cross_file_state_field_conflict",
1355
+ moduleIndex: null,
1356
+ unitIndex: null,
1357
+ fixInstruction:
1358
+ "Use ONE canonical approach for state fields. If other files use 'deletedAt: datetime', do NOT use 'isDeleted: boolean'. Pick one and align.",
1359
+ evidence: detail,
1360
+ })),
1361
+ ...props.fileErrorCodeConflicts.map((detail) => ({
1362
+ ruleCode: "cross_file_error_code_conflict",
1363
+ moduleIndex: null,
1364
+ unitIndex: null,
1365
+ fixInstruction:
1366
+ "Use the canonical error code defined in the first file. Do NOT invent alternative error codes for the same condition.",
1367
+ evidence: detail,
1368
+ })),
1369
+ ...props.fileOversizedToc.map((detail) => ({
1370
+ ruleCode: "oversized_toc",
1371
+ moduleIndex: null,
1372
+ unitIndex: null,
1373
+ fixInstruction:
1374
+ "TOC must be a concise navigation aid. Remove detailed requirements, keep only navigation tables and brief summaries.",
1375
+ evidence: detail,
1376
+ })),
1377
+ ...props.fileProseConflicts.map((detail) => ({
1378
+ ruleCode: "cross_file_prose_constraint_conflict",
1379
+ moduleIndex: null,
1380
+ unitIndex: null,
1381
+ fixInstruction:
1382
+ "Remove the restated constraint value and use a backtick reference to the canonical definition in 02-domain-model instead. Example: 'THE system SHALL validate `User.bio` per entity constraints (see 02-domain-model)'",
1383
+ evidence: detail,
1384
+ })),
1385
+ ];
1386
+ }
1387
+
1388
+ function buildSectionIndicesPerUnit(
1389
+ issues: AutoBeAnalyzeSectionReviewIssue[],
1390
+ moduleIndex: number,
1391
+ unitIndices: number[],
1392
+ ): Record<number, number[]> | null {
1393
+ const map: Record<number, Set<number>> = {};
1394
+ let hasSectionLevel = false;
1395
+
1396
+ for (const issue of issues) {
1397
+ if (
1398
+ issue.moduleIndex === moduleIndex &&
1399
+ issue.unitIndex !== null &&
1400
+ unitIndices.includes(issue.unitIndex) &&
1401
+ issue.sectionIndex !== null &&
1402
+ issue.sectionIndex !== undefined &&
1403
+ Number.isInteger(issue.sectionIndex) &&
1404
+ issue.sectionIndex >= 0
1405
+ ) {
1406
+ if (!map[issue.unitIndex]) map[issue.unitIndex] = new Set();
1407
+ map[issue.unitIndex]!.add(issue.sectionIndex);
1408
+ hasSectionLevel = true;
1409
+ }
1410
+ }
1411
+
1412
+ if (!hasSectionLevel) return null;
1413
+
1414
+ const result: Record<number, number[]> = {};
1415
+ for (const [ui, sectionSet] of Object.entries(map)) {
1416
+ result[Number(ui)] = [...sectionSet].sort((a, b) => a - b);
1417
+ }
1418
+ return result;
1419
+ }
1420
+
1421
+ function normalizeRejectedModuleUnits(
1422
+ rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1423
+ fileIssues: AutoBeAnalyzeSectionReviewIssue[],
1424
+ ): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
1425
+ if (rejected == null) return null;
1426
+ return rejected.map((entry) => {
1427
+ const enrichedIssues =
1428
+ (entry.issues?.length ?? 0) > 0
1429
+ ? dedupeReviewIssues(entry.issues ?? [])
1430
+ : dedupeReviewIssues(
1431
+ fileIssues.filter(
1432
+ (issue) =>
1433
+ issue.moduleIndex === entry.moduleIndex &&
1434
+ (issue.unitIndex === null ||
1435
+ entry.unitIndices.includes(issue.unitIndex)),
1436
+ ),
1437
+ );
1438
+
1439
+ const sectionIndicesPerUnit =
1440
+ entry.sectionIndicesPerUnit ??
1441
+ buildSectionIndicesPerUnit(
1442
+ enrichedIssues,
1443
+ entry.moduleIndex,
1444
+ entry.unitIndices,
1445
+ );
1446
+
1447
+ return {
1448
+ ...entry,
1449
+ issues: enrichedIssues,
1450
+ sectionIndicesPerUnit,
1451
+ };
1452
+ });
1453
+ }
1454
+
1455
+ /**
1456
+ * Infer rejectedModuleUnits from structured issues when the LLM review didn't
1457
+ * provide explicit rejection targets. This prevents full-file rewrites when
1458
+ * only specific module/unit pairs have issues.
1459
+ */
1460
+ function inferRejectedModuleUnitsFromIssues(
1461
+ issues: AutoBeAnalyzeSectionReviewIssue[],
1462
+ unitResults: AutoBeAnalyzeWriteUnitEvent[],
1463
+ ): AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null {
1464
+ const moduleUnitMap = new Map<number, Set<number>>();
1465
+ let hasTargetedIssue = false;
1466
+
1467
+ for (const issue of issues) {
1468
+ if (issue.moduleIndex !== null && issue.moduleIndex !== undefined) {
1469
+ hasTargetedIssue = true;
1470
+ if (!moduleUnitMap.has(issue.moduleIndex)) {
1471
+ moduleUnitMap.set(issue.moduleIndex, new Set());
1472
+ }
1473
+ if (issue.unitIndex !== null && issue.unitIndex !== undefined) {
1474
+ moduleUnitMap.get(issue.moduleIndex)!.add(issue.unitIndex);
1475
+ }
1476
+ }
1477
+ }
1478
+
1479
+ if (!hasTargetedIssue) return null;
1480
+
1481
+ const result: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] = [];
1482
+ for (const [moduleIndex, unitIndexSet] of moduleUnitMap) {
1483
+ // If no specific units targeted, include all units for this module
1484
+ let unitIndices: number[];
1485
+ if (unitIndexSet.size === 0) {
1486
+ const unitEvent = unitResults[moduleIndex];
1487
+ unitIndices = unitEvent
1488
+ ? Array.from({ length: unitEvent.unitSections.length }, (_, i) => i)
1489
+ : [];
1490
+ } else {
1491
+ unitIndices = [...unitIndexSet].sort((a, b) => a - b);
1492
+ }
1493
+
1494
+ const moduleIssues = dedupeReviewIssues(
1495
+ issues.filter(
1496
+ (i) =>
1497
+ i.moduleIndex === moduleIndex &&
1498
+ (i.unitIndex === null || unitIndices.includes(i.unitIndex)),
1499
+ ),
1500
+ );
1501
+
1502
+ result.push({
1503
+ moduleIndex,
1504
+ unitIndices,
1505
+ feedback: moduleIssues.map((i) => i.fixInstruction).join("; "),
1506
+ issues: moduleIssues,
1507
+ sectionIndicesPerUnit: buildSectionIndicesPerUnit(
1508
+ moduleIssues,
1509
+ moduleIndex,
1510
+ unitIndices,
1511
+ ),
1512
+ });
1513
+ }
1514
+
1515
+ return result.length > 0 ? result : null;
1516
+ }
1517
+
1518
+ function formatStructuredIssuesForRetry(props: {
1519
+ fallbackFeedback: string;
1520
+ issues: AutoBeAnalyzeSectionReviewIssue[];
1521
+ }): string {
1522
+ if (props.issues.length === 0) return props.fallbackFeedback;
1523
+ const lines = props.issues.map(
1524
+ (issue) =>
1525
+ `- [${issue.ruleCode}] target=${formatIssueTarget(issue)} fix=${issue.fixInstruction}` +
1526
+ (issue.evidence ? ` | evidence=${issue.evidence}` : ""),
1527
+ );
1528
+ return `${props.fallbackFeedback}\n\n[STRUCTURED REVIEW ISSUES]\n${lines.join("\n")}`.trim();
1529
+ }
1530
+
1531
+ function formatIssueTarget(
1532
+ issue: Pick<AutoBeAnalyzeSectionReviewIssue, "moduleIndex" | "unitIndex"> &
1533
+ Partial<Pick<AutoBeAnalyzeSectionReviewIssue, "sectionIndex">>,
1534
+ ): string {
1535
+ const parts: string[] = [];
1536
+ if (issue.moduleIndex !== null && issue.moduleIndex !== undefined)
1537
+ parts.push(`m${issue.moduleIndex}`);
1538
+ if (issue.unitIndex !== null && issue.unitIndex !== undefined)
1539
+ parts.push(`u${issue.unitIndex}`);
1540
+ if (issue.sectionIndex !== null && issue.sectionIndex !== undefined)
1541
+ parts.push(`s${issue.sectionIndex}`);
1542
+ return parts.length ? parts.join(".") : "file";
1543
+ }
1544
+
1545
+ function dedupeReviewIssues(
1546
+ issues: AutoBeAnalyzeSectionReviewIssue[],
1547
+ ): AutoBeAnalyzeSectionReviewIssue[] {
1548
+ const map = new Map<string, AutoBeAnalyzeSectionReviewIssue>();
1549
+ for (const issue of issues) {
1550
+ const key = [
1551
+ issue.ruleCode,
1552
+ issue.moduleIndex ?? "x",
1553
+ issue.unitIndex ?? "x",
1554
+ issue.sectionIndex ?? "x",
1555
+ issue.fixInstruction,
1556
+ ].join("|");
1557
+ if (!map.has(key)) map.set(key, issue);
1558
+ }
1559
+ return [...map.values()];
1560
+ }
1561
+
1562
+ // ─── Per-module review helpers ───
1563
+
1564
+ function buildSiblingModuleSummaries(
1565
+ moduleEvent: AutoBeAnalyzeWriteModuleEvent,
1566
+ sectionResults: AutoBeAnalyzeWriteSectionEvent[][],
1567
+ excludeModuleIndex: number,
1568
+ ): Array<{
1569
+ moduleIndex: number;
1570
+ title: string;
1571
+ sectionTitles: string[];
1572
+ }> {
1573
+ return sectionResults
1574
+ .map((sectionsForModule, moduleIndex) => ({
1575
+ moduleIndex,
1576
+ title: moduleEvent.moduleSections[moduleIndex]?.title ?? "Unknown",
1577
+ sectionTitles: sectionsForModule.flatMap((se) =>
1578
+ se.sectionSections.map((s) => s.title),
1579
+ ),
1580
+ }))
1581
+ .filter((s) => s.moduleIndex !== excludeModuleIndex);
1582
+ }
1583
+
1584
+ function mergeModuleReviewEvents(
1585
+ moduleReviews: AutoBeAnalyzeSectionReviewEvent[],
1586
+ fileIndex: number,
1587
+ ): AutoBeAnalyzeSectionReviewEvent | null {
1588
+ if (moduleReviews.length === 0) return null;
1589
+
1590
+ const allApproved = moduleReviews.every(
1591
+ (r) => r.fileResults[0]?.approved ?? true,
1592
+ );
1593
+ const allFeedback = moduleReviews
1594
+ .map((r) => r.fileResults[0]?.feedback)
1595
+ .filter(Boolean)
1596
+ .join("\n");
1597
+ const allRejectedModuleUnits = moduleReviews.flatMap(
1598
+ (r) => r.fileResults[0]?.rejectedModuleUnits ?? [],
1599
+ );
1600
+ const allIssues = moduleReviews.flatMap(
1601
+ (r) => r.fileResults[0]?.issues ?? [],
1602
+ );
1603
+
1604
+ // Use the last review event as base (for tokenUsage, metric, etc.)
1605
+ const base = moduleReviews[moduleReviews.length - 1]!;
1606
+ return {
1607
+ ...base,
1608
+ fileResults: [
1609
+ {
1610
+ fileIndex,
1611
+ approved: allApproved,
1612
+ feedback: allFeedback,
1613
+ revisedSections: null,
1614
+ rejectedModuleUnits:
1615
+ allRejectedModuleUnits.length > 0 ? allRejectedModuleUnits : null,
1616
+ issues: allIssues.length > 0 ? allIssues : null,
1617
+ },
1618
+ ],
1619
+ };
1620
+ }
1621
+
1622
+ // ─── Section-stage helpers ───
1623
+
1624
+ function buildSectionContentSignature(state: IFileState): string {
1625
+ if (!state.sectionResults) return "none";
1626
+ return JSON.stringify(
1627
+ state.sectionResults.map((moduleSections) =>
1628
+ moduleSections.map((unit) =>
1629
+ unit.sectionSections.map((section) => ({
1630
+ title: section.title,
1631
+ // content text included to detect no-progress rewrites
1632
+ content: section.content,
1633
+ })),
1634
+ ),
1635
+ ),
1636
+ );
1637
+ }
1638
+
1639
+ function buildSectionRejectionSignature(props: {
1640
+ rejectedModuleUnits:
1641
+ | AutoBeAnalyzeSectionReviewRejectedModuleUnit[]
1642
+ | null
1643
+ | undefined;
1644
+ feedback: string;
1645
+ }): string {
1646
+ return JSON.stringify({
1647
+ rejectedModuleUnits: (props.rejectedModuleUnits ?? []).map((entry) => ({
1648
+ moduleIndex: entry.moduleIndex,
1649
+ unitIndices: [...entry.unitIndices].sort((a, b) => a - b),
1650
+ feedback: entry.feedback,
1651
+ issues: (entry.issues ?? []).map((issue) => ({
1652
+ ruleCode: issue.ruleCode,
1653
+ moduleIndex: issue.moduleIndex,
1654
+ unitIndex: issue.unitIndex,
1655
+ sectionIndex: issue.sectionIndex ?? null,
1656
+ fixInstruction: issue.fixInstruction,
1657
+ })),
1658
+ })),
1659
+ feedback: props.feedback,
1660
+ });
1661
+ }
1662
+
1663
+ function formatRejectedModuleUnitsSummary(
1664
+ rejected: AutoBeAnalyzeSectionReviewRejectedModuleUnit[] | null | undefined,
1665
+ ): string {
1666
+ if (!rejected || rejected.length === 0) return "all-or-unknown";
1667
+ return rejected
1668
+ .slice(0, 6)
1669
+ .map((entry) => {
1670
+ const unitParts = entry.unitIndices.map((ui) => {
1671
+ const sectionPart = entry.sectionIndicesPerUnit?.[ui];
1672
+ return sectionPart ? `u${ui}(s${sectionPart.join(",s")})` : `u${ui}`;
1673
+ });
1674
+ return `m${entry.moduleIndex}:${unitParts.join(",") || "-"}`;
1675
+ })
1676
+ .join(" | ");
1677
+ }
1678
+
1679
+ function formatReviewIssuesSummary(
1680
+ issues: AutoBeAnalyzeSectionReviewIssue[],
1681
+ ): string {
1682
+ if (issues.length === 0) return "none";
1683
+ return issues
1684
+ .slice(0, 8)
1685
+ .map((issue) => `${issue.ruleCode}@${formatIssueTarget(issue)}`)
1686
+ .join(", ");
1687
+ }
1688
+
1689
+ function truncateForDebug(text: string, max: number): string {
1690
+ const singleLine = text.replace(/\s+/g, " ").trim();
1691
+ if (singleLine.length <= max) return singleLine;
1692
+ return `${singleLine.slice(0, max)}...`;
1693
+ }