okstra 0.114.0 → 0.116.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 (337) hide show
  1. package/docs/kr/architecture.md +2 -2
  2. package/docs/kr/follow-ups/2026-07-10-final-report-option-3.md +51 -0
  3. package/package.json +1 -1
  4. package/runtime/BUILD.json +2 -2
  5. package/runtime/agents/workers/report-writer-worker.md +1 -0
  6. package/runtime/prompts/launch.template.md +34 -0
  7. package/runtime/prompts/lead/convergence.md +2 -0
  8. package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
  9. package/runtime/prompts/lead/report-writer.md +1 -1
  10. package/runtime/prompts/profiles/implementation-planning.md +1 -0
  11. package/runtime/python/okstra_ctl/implementation_stage.py +46 -39
  12. package/runtime/python/okstra_ctl/incremental_carry.py +70 -0
  13. package/runtime/python/okstra_ctl/incremental_scope.py +89 -0
  14. package/runtime/python/okstra_ctl/report_views.py +151 -12
  15. package/runtime/python/okstra_ctl/run.py +24 -24
  16. package/runtime/python/okstra_ctl/stage_targets.py +26 -0
  17. package/runtime/python/okstra_ctl/worktree.py +1 -1
  18. package/runtime/python/okstra_token_usage/claude.py +58 -19
  19. package/runtime/python/okstra_token_usage/collect.py +3 -1
  20. package/runtime/python/okstra_vendor/__init__.py +3 -0
  21. package/runtime/schemas/final-report-v1.0.schema.json +39 -0
  22. package/runtime/templates/reports/final-report.template.md +27 -0
  23. package/runtime/templates/reports/i18n/en.json +23 -12
  24. package/runtime/templates/reports/i18n/ko.json +23 -12
  25. package/runtime/templates/reports/report.css +55 -0
  26. package/runtime/templates/reports/report.js +53 -1
  27. package/runtime/validators/validate-run.py +50 -0
  28. package/src/cli-registry.mjs +14 -0
  29. package/src/commands/execute/incremental-carry.mjs +20 -0
  30. package/src/commands/execute/incremental-scope.mjs +20 -0
  31. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/__init__.py +0 -0
  32. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py +0 -41
  33. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_clique.py +0 -112
  34. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_connectivity.py +0 -199
  35. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_density.py +0 -146
  36. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_distance_measures.py +0 -59
  37. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_dominating_set.py +0 -78
  38. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_kcomponents.py +0 -303
  39. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_matching.py +0 -8
  40. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_maxcut.py +0 -94
  41. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_ramsey.py +0 -31
  42. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_steinertree.py +0 -306
  43. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_traveling_salesman.py +0 -1014
  44. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_treewidth.py +0 -274
  45. package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_vertex_cover.py +0 -68
  46. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/__init__.py +0 -0
  47. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/base_test.py +0 -81
  48. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_connectivity.py +0 -143
  49. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_correlation.py +0 -122
  50. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_mixing.py +0 -174
  51. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_neighbor_degree.py +0 -107
  52. package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_pairs.py +0 -87
  53. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/__init__.py +0 -0
  54. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_basic.py +0 -125
  55. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_centrality.py +0 -192
  56. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_cluster.py +0 -84
  57. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_covering.py +0 -33
  58. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_edgelist.py +0 -240
  59. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_extendability.py +0 -334
  60. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_generators.py +0 -407
  61. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_link_analysis.py +0 -218
  62. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matching.py +0 -327
  63. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matrix.py +0 -138
  64. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_project.py +0 -409
  65. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_redundancy.py +0 -35
  66. package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_spectral_bipartivity.py +0 -80
  67. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/__init__.py +0 -0
  68. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality.py +0 -923
  69. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py +0 -354
  70. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_closeness_centrality.py +0 -274
  71. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py +0 -259
  72. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py +0 -147
  73. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_closeness.py +0 -43
  74. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_degree_centrality.py +0 -144
  75. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_dispersion.py +0 -73
  76. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py +0 -186
  77. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_group.py +0 -277
  78. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_harmonic_centrality.py +0 -122
  79. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_katz_centrality.py +0 -345
  80. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_laplacian_centrality.py +0 -220
  81. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_load_centrality.py +0 -344
  82. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_percolation_centrality.py +0 -87
  83. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_reaching.py +0 -140
  84. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_second_order_centrality.py +0 -82
  85. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_subgraph.py +0 -110
  86. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_trophic.py +0 -302
  87. package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_voterank.py +0 -64
  88. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/__init__.py +0 -0
  89. package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/test_coloring.py +0 -863
  90. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/__init__.py +0 -0
  91. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_asyn_fluid.py +0 -147
  92. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_bipartitions.py +0 -157
  93. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_centrality.py +0 -85
  94. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_divisive.py +0 -106
  95. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_kclique.py +0 -91
  96. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_label_propagation.py +0 -241
  97. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_leiden.py +0 -138
  98. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_local.py +0 -76
  99. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_louvain.py +0 -264
  100. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_lukes.py +0 -152
  101. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_modularity_max.py +0 -340
  102. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_quality.py +0 -139
  103. package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_utils.py +0 -26
  104. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/__init__.py +0 -0
  105. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_attracting.py +0 -70
  106. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_biconnected.py +0 -248
  107. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_connected.py +0 -138
  108. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_semiconnected.py +0 -55
  109. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_strongly_connected.py +0 -193
  110. package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_weakly_connected.py +0 -96
  111. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/__init__.py +0 -0
  112. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_connectivity.py +0 -421
  113. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_cuts.py +0 -309
  114. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_disjoint_paths.py +0 -249
  115. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_augmentation.py +0 -502
  116. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py +0 -488
  117. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcomponents.py +0 -323
  118. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcutsets.py +0 -280
  119. package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_stoer_wagner.py +0 -102
  120. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/__init__.py +0 -0
  121. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gl1.gpickle.bz2 +0 -0
  122. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gw1.gpickle.bz2 +0 -0
  123. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/netgen-2.gpickle.bz2 +0 -0
  124. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_gomory_hu.py +0 -128
  125. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow.py +0 -573
  126. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow_large_graph.py +0 -155
  127. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_mincost.py +0 -475
  128. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_networksimplex.py +0 -481
  129. package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/wlm3.gpickle.bz2 +0 -0
  130. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/__init__.py +0 -0
  131. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.A99 +0 -0
  132. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.B99 +0 -0
  133. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.A99 +0 -0
  134. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.B99 +0 -0
  135. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_ismags.py +0 -719
  136. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphism.py +0 -103
  137. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphvf2.py +0 -490
  138. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_match_helpers.py +0 -64
  139. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_temporalisomorphvf2.py +0 -212
  140. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_tree_isomorphism.py +0 -202
  141. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp.py +0 -1655
  142. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp_helpers.py +0 -3118
  143. package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2userfunc.py +0 -196
  144. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/__init__.py +0 -0
  145. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_hits.py +0 -77
  146. package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_pagerank.py +0 -213
  147. package/runtime/python/okstra_vendor/networkx/algorithms/minors/tests/test_contraction.py +0 -544
  148. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/__init__.py +0 -0
  149. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_all.py +0 -328
  150. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_binary.py +0 -451
  151. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_product.py +0 -491
  152. package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_unary.py +0 -55
  153. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/__init__.py +0 -0
  154. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_astar.py +0 -254
  155. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense.py +0 -212
  156. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py +0 -88
  157. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_generic.py +0 -511
  158. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_unweighted.py +0 -149
  159. package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_weighted.py +0 -983
  160. package/runtime/python/okstra_vendor/networkx/algorithms/tests/__init__.py +0 -0
  161. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_asteroidal.py +0 -23
  162. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_boundary.py +0 -154
  163. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_bridges.py +0 -144
  164. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_broadcasting.py +0 -109
  165. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chains.py +0 -136
  166. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chordal.py +0 -129
  167. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_clique.py +0 -300
  168. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cluster.py +0 -678
  169. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_communicability.py +0 -80
  170. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_core.py +0 -266
  171. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_covering.py +0 -85
  172. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cuts.py +0 -171
  173. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cycles.py +0 -984
  174. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_d_separation.py +0 -340
  175. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dag.py +0 -835
  176. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_measures.py +0 -831
  177. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_regular.py +0 -85
  178. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominance.py +0 -299
  179. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominating.py +0 -115
  180. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_efficiency.py +0 -58
  181. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_euler.py +0 -314
  182. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graph_hashing.py +0 -872
  183. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graphical.py +0 -163
  184. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hierarchy.py +0 -46
  185. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hybrid.py +0 -24
  186. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_isolate.py +0 -26
  187. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_link_prediction.py +0 -615
  188. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_lowest_common_ancestors.py +0 -459
  189. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_matching.py +0 -556
  190. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_max_weight_clique.py +0 -179
  191. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_mis.py +0 -62
  192. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_moral.py +0 -15
  193. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_node_classification.py +0 -140
  194. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_non_randomness.py +0 -60
  195. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_perfect_graph.py +0 -27
  196. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planar_drawing.py +0 -274
  197. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planarity.py +0 -556
  198. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_polynomials.py +0 -57
  199. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_reciprocity.py +0 -37
  200. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_regular.py +0 -88
  201. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_richclub.py +0 -149
  202. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_similarity.py +0 -1158
  203. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_simple_paths.py +0 -803
  204. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smallworld.py +0 -76
  205. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smetric.py +0 -8
  206. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_sparsifiers.py +0 -138
  207. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_structuralholes.py +0 -191
  208. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_summarization.py +0 -642
  209. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_swap.py +0 -179
  210. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_threshold.py +0 -270
  211. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_time_dependent.py +0 -431
  212. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_tournament.py +0 -161
  213. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_triads.py +0 -248
  214. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_vitality.py +0 -41
  215. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_voronoi.py +0 -103
  216. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_walks.py +0 -54
  217. package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_wiener.py +0 -157
  218. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/__init__.py +0 -0
  219. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_beamsearch.py +0 -25
  220. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_bfs.py +0 -203
  221. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_dfs.py +0 -307
  222. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgebfs.py +0 -147
  223. package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgedfs.py +0 -131
  224. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/__init__.py +0 -0
  225. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_branchings.py +0 -624
  226. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_coding.py +0 -114
  227. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_decomposition.py +0 -79
  228. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_distance_measures.py +0 -99
  229. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_mst.py +0 -934
  230. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_operations.py +0 -53
  231. package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_recognition.py +0 -174
  232. package/runtime/python/okstra_vendor/networkx/classes/tests/__init__.py +0 -0
  233. package/runtime/python/okstra_vendor/networkx/classes/tests/dispatch_interface.py +0 -192
  234. package/runtime/python/okstra_vendor/networkx/classes/tests/historical_tests.py +0 -476
  235. package/runtime/python/okstra_vendor/networkx/classes/tests/test_coreviews.py +0 -362
  236. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph.py +0 -331
  237. package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph_historical.py +0 -110
  238. package/runtime/python/okstra_vendor/networkx/classes/tests/test_filters.py +0 -177
  239. package/runtime/python/okstra_vendor/networkx/classes/tests/test_function.py +0 -1045
  240. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph.py +0 -950
  241. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph_historical.py +0 -12
  242. package/runtime/python/okstra_vendor/networkx/classes/tests/test_graphviews.py +0 -349
  243. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multidigraph.py +0 -459
  244. package/runtime/python/okstra_vendor/networkx/classes/tests/test_multigraph.py +0 -528
  245. package/runtime/python/okstra_vendor/networkx/classes/tests/test_reportviews.py +0 -1421
  246. package/runtime/python/okstra_vendor/networkx/classes/tests/test_special.py +0 -131
  247. package/runtime/python/okstra_vendor/networkx/classes/tests/test_subgraphviews.py +0 -371
  248. package/runtime/python/okstra_vendor/networkx/conftest.py +0 -261
  249. package/runtime/python/okstra_vendor/networkx/drawing/tests/__init__.py +0 -0
  250. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_complex.png +0 -0
  251. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_empty_graph.png +0 -0
  252. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_house_with_colors.png +0 -0
  253. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_labels_and_colors.png +0 -0
  254. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_shortest_path.png +0 -0
  255. package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_house_with_colors.png +0 -0
  256. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_agraph.py +0 -237
  257. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_image_comparison_pylab_mpl.py +0 -229
  258. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_latex.py +0 -285
  259. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_layout.py +0 -631
  260. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pydot.py +0 -146
  261. package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pylab.py +0 -1582
  262. package/runtime/python/okstra_vendor/networkx/generators/tests/__init__.py +0 -0
  263. package/runtime/python/okstra_vendor/networkx/generators/tests/test_atlas.py +0 -75
  264. package/runtime/python/okstra_vendor/networkx/generators/tests/test_classic.py +0 -642
  265. package/runtime/python/okstra_vendor/networkx/generators/tests/test_cographs.py +0 -20
  266. package/runtime/python/okstra_vendor/networkx/generators/tests/test_community.py +0 -362
  267. package/runtime/python/okstra_vendor/networkx/generators/tests/test_degree_seq.py +0 -224
  268. package/runtime/python/okstra_vendor/networkx/generators/tests/test_directed.py +0 -189
  269. package/runtime/python/okstra_vendor/networkx/generators/tests/test_duplication.py +0 -103
  270. package/runtime/python/okstra_vendor/networkx/generators/tests/test_ego.py +0 -39
  271. package/runtime/python/okstra_vendor/networkx/generators/tests/test_expanders.py +0 -182
  272. package/runtime/python/okstra_vendor/networkx/generators/tests/test_geometric.py +0 -488
  273. package/runtime/python/okstra_vendor/networkx/generators/tests/test_harary_graph.py +0 -133
  274. package/runtime/python/okstra_vendor/networkx/generators/tests/test_internet_as_graphs.py +0 -221
  275. package/runtime/python/okstra_vendor/networkx/generators/tests/test_intersection.py +0 -28
  276. package/runtime/python/okstra_vendor/networkx/generators/tests/test_interval_graph.py +0 -144
  277. package/runtime/python/okstra_vendor/networkx/generators/tests/test_joint_degree_seq.py +0 -125
  278. package/runtime/python/okstra_vendor/networkx/generators/tests/test_lattice.py +0 -264
  279. package/runtime/python/okstra_vendor/networkx/generators/tests/test_line.py +0 -316
  280. package/runtime/python/okstra_vendor/networkx/generators/tests/test_mycielski.py +0 -30
  281. package/runtime/python/okstra_vendor/networkx/generators/tests/test_nonisomorphic_trees.py +0 -82
  282. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_clustered.py +0 -33
  283. package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_graphs.py +0 -495
  284. package/runtime/python/okstra_vendor/networkx/generators/tests/test_small.py +0 -220
  285. package/runtime/python/okstra_vendor/networkx/generators/tests/test_spectral_graph_forge.py +0 -49
  286. package/runtime/python/okstra_vendor/networkx/generators/tests/test_stochastic.py +0 -72
  287. package/runtime/python/okstra_vendor/networkx/generators/tests/test_sudoku.py +0 -92
  288. package/runtime/python/okstra_vendor/networkx/generators/tests/test_time_series.py +0 -64
  289. package/runtime/python/okstra_vendor/networkx/generators/tests/test_trees.py +0 -195
  290. package/runtime/python/okstra_vendor/networkx/generators/tests/test_triads.py +0 -15
  291. package/runtime/python/okstra_vendor/networkx/linalg/tests/__init__.py +0 -0
  292. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_algebraic_connectivity.py +0 -400
  293. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_attrmatrix.py +0 -108
  294. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_bethehessian.py +0 -40
  295. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_graphmatrix.py +0 -275
  296. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_laplacian.py +0 -334
  297. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_modularity.py +0 -86
  298. package/runtime/python/okstra_vendor/networkx/linalg/tests/test_spectrum.py +0 -70
  299. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/__init__.py +0 -0
  300. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_adjacency.py +0 -78
  301. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_cytoscape.py +0 -78
  302. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_node_link.py +0 -109
  303. package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_tree.py +0 -48
  304. package/runtime/python/okstra_vendor/networkx/readwrite/tests/__init__.py +0 -0
  305. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_adjlist.py +0 -354
  306. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_edgelist.py +0 -318
  307. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gexf.py +0 -612
  308. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gml.py +0 -744
  309. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graph6.py +0 -181
  310. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graphml.py +0 -1531
  311. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_leda.py +0 -30
  312. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_p2g.py +0 -63
  313. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_pajek.py +0 -128
  314. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_sparse6.py +0 -166
  315. package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_text.py +0 -1742
  316. package/runtime/python/okstra_vendor/networkx/tests/__init__.py +0 -0
  317. package/runtime/python/okstra_vendor/networkx/tests/test_all_random_functions.py +0 -248
  318. package/runtime/python/okstra_vendor/networkx/tests/test_convert.py +0 -321
  319. package/runtime/python/okstra_vendor/networkx/tests/test_convert_numpy.py +0 -531
  320. package/runtime/python/okstra_vendor/networkx/tests/test_convert_pandas.py +0 -349
  321. package/runtime/python/okstra_vendor/networkx/tests/test_convert_scipy.py +0 -281
  322. package/runtime/python/okstra_vendor/networkx/tests/test_exceptions.py +0 -40
  323. package/runtime/python/okstra_vendor/networkx/tests/test_import.py +0 -11
  324. package/runtime/python/okstra_vendor/networkx/tests/test_lazy_imports.py +0 -96
  325. package/runtime/python/okstra_vendor/networkx/tests/test_relabel.py +0 -349
  326. package/runtime/python/okstra_vendor/networkx/tests/test_removed_functions_exception_messages.py +0 -8
  327. package/runtime/python/okstra_vendor/networkx/utils/tests/__init__.py +0 -0
  328. package/runtime/python/okstra_vendor/networkx/utils/tests/test__init.py +0 -11
  329. package/runtime/python/okstra_vendor/networkx/utils/tests/test_backends.py +0 -225
  330. package/runtime/python/okstra_vendor/networkx/utils/tests/test_config.py +0 -263
  331. package/runtime/python/okstra_vendor/networkx/utils/tests/test_decorators.py +0 -510
  332. package/runtime/python/okstra_vendor/networkx/utils/tests/test_heaps.py +0 -131
  333. package/runtime/python/okstra_vendor/networkx/utils/tests/test_mapped_queue.py +0 -268
  334. package/runtime/python/okstra_vendor/networkx/utils/tests/test_misc.py +0 -393
  335. package/runtime/python/okstra_vendor/networkx/utils/tests/test_random_sequence.py +0 -53
  336. package/runtime/python/okstra_vendor/networkx/utils/tests/test_rcm.py +0 -63
  337. package/runtime/python/okstra_vendor/networkx/utils/tests/test_unionfind.py +0 -55
@@ -235,7 +235,7 @@ per-process 환경 변수에 task 정체성·경로·workflow 상태를 보관
235
235
  - standard workflow의 기본 worker role은 `Claude worker`, `Codex worker`, `Report writer worker`이며, `Antigravity worker`는 `--workers` 또는 프로필에서 명시할 때만 포함되는 옵션입니다.
236
236
  - worker 역할 분담과 최종 판단은 Claude가 task bundle을 읽고 수행합니다.
237
237
  - 사용자 홈에 설치된 okstra Claude assets(`~/.claude/skills`, `~/.claude/agents`) 는 `Agent(name: ...)` 로 워커를 dispatch 하도록 Claude 를 유도합니다 — 워커는 세션의 implicit team 에 자동 합류합니다.
238
- - **팀 lifecycle (Claude Code v2.1.178+)**: v2.1.178 이 `TeamCreate` / `TeamDelete` 도구와 `Agent(...)` 의 `team_name` 파라미터를 제거했습니다. `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`(`okstra install` 이 `settings.json` 에 시드) 이면 세션마다 implicit team 1개가 시작 시점에 자동 생성됩니다. lead 는 Phase 3 에서 팀을 만드는 도구를 호출하지 않고, `teamName` audit 라벨과 `teamCreate: { attempted: false, status: "implicit", splitPane: <$TMUX 유무> }` 만 기록한 뒤 워커를 `Agent(name: "<role>-worker", run_in_background: true)`(team_name 없음)로 dispatch 합니다. split-pane teammate 는 `$TMUX` 가 설정돼 있고 `teammateMode: auto` 일 때 나타나며, tmux 밖이면 in-process 로 돕니다(둘 다 정상). run 종료 시 Phase 7 토큰 집계 이후 잔여 tmux pane 정리를 확인하고, split-pane run 에서만 worker teammate 를 정리할지 확인합니다. 승인 시 `okstra-team-reconcile.sh` 로 dead-pane stale-active 멤버를 inactive 로 정리하고 각 완료 teammate 에 `SendMessage` shutdown_request 를 보냅니다 — implicit team 자체를 지우는 도구는 없으며, 팀은 세션 종료와 함께 사라집니다. 사용자가 유지하면 teammate 는 FleetView roster 에 남고, lead 는 Teams/FleetView 에서 제거하라고 안내합니다 (`prompts/profiles/_common-contract.md` 의 *Run-end teammate teardown*). Phase 7 토큰 집계는 `teamCreate.status` 가 `implicit`/`skipped`/`error` 일 때 `agentName` 기반으로 워커 세션을 찾습니다(implicit team 은 jsonl`session-<leadSid>` 태깅하므로 okstra `teamName` 라벨 needle 만으로는 lead 만 잡힘).
238
+ - **팀 lifecycle (Claude Code v2.1.178+)**: v2.1.178 이 `TeamCreate` / `TeamDelete` 도구와 `Agent(...)` 의 `team_name` 파라미터를 제거했습니다. `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`(`okstra install` 이 `settings.json` 에 시드) 이면 세션마다 implicit team 1개가 시작 시점에 자동 생성됩니다. lead 는 Phase 3 에서 팀을 만드는 도구를 호출하지 않고, `teamName` audit 라벨과 `teamCreate: { attempted: false, status: "implicit", splitPane: <$TMUX 유무> }` 만 기록한 뒤 워커를 `Agent(name: "<role>-worker", run_in_background: true)`(team_name 없음)로 dispatch 합니다. split-pane teammate 는 `$TMUX` 가 설정돼 있고 `teammateMode: auto` 일 때 나타나며, tmux 밖이면 in-process 로 돕니다(둘 다 정상). run 종료 시 Phase 7 토큰 집계 이후 잔여 tmux pane 정리를 확인하고, split-pane run 에서만 worker teammate 를 정리할지 확인합니다. 승인 시 `okstra-team-reconcile.sh` 로 dead-pane stale-active 멤버를 inactive 로 정리하고 각 완료 teammate 에 `SendMessage` shutdown_request 를 보냅니다 — implicit team 자체를 지우는 도구는 없으며, 팀은 세션 종료와 함께 사라집니다. 사용자가 유지하면 teammate 는 FleetView roster 에 남고, lead 는 Teams/FleetView 에서 제거하라고 안내합니다 (`prompts/profiles/_common-contract.md` 의 *Run-end teammate teardown*). Phase 7 토큰 집계는 `teamCreate.status` 가 `implicit`/`skipped`/`error` 일 때 top-level `agentName` 또는 nested `subagents/agent-a<name>-<hash>.jsonl` 파일명 기반으로 워커 세션을 찾습니다.
239
239
 
240
240
  ## Claude prompt contract
241
241
 
@@ -698,7 +698,7 @@ Phase 7 step 1.5 가 final-report MD 한 본을 입력으로 self-contained HTML
698
698
 
699
699
  - Helper CLI: `scripts/okstra-token-usage.py`
700
700
  - 수집 소스:
701
- - Claude lead/workers: `~/.claude/projects/<cwd-as-dashes>/<sessionId>.jsonl`의 per-message `message.usage`
701
+ - Claude lead/workers: `~/.claude/projects/<cwd-as-dashes>/<sessionId>.jsonl` 또는 `~/.claude/projects/<cwd-as-dashes>/<lead-session>/subagents/agent-a<worker-name>-<hash>.jsonl` 의 per-message `message.usage`. nested subagent 파일은 파일명에서 worker name 을 복원하고, 현재 run 의 `team-state.lead.sessionId` 디렉터리 아래만 집계합니다.
702
702
  - Codex CLI: `~/.agent/sessions/Y/M/D/rollout-*.jsonl`의 마지막 `total_token_usage.total_tokens`
703
703
  - Antigravity CLI: `~/.antigravity/tmp/*/chats/session-*.json`의 per-message `tokens.total`
704
704
  - billable-equivalent token math와 USD cost estimation을 함께 기록합니다. Anthropic billing ratio(`cache_creation_5m=1.25x`, `cache_creation_1h=2.0x`, `cache_read=0.1x`, `output=5x`)를 반영합니다. transcript 의 `usage.cache_creation.ephemeral_5m_input_tokens` / `ephemeral_1h_input_tokens` 분해가 있으면 분리 집계합니다.
@@ -0,0 +1,51 @@
1
+ # Final Report 가독성 후속: 3안 재검토
2
+
3
+ 상태: 보류
4
+ 작성일: 2026-07-10
5
+
6
+ ## 배경
7
+
8
+ 이번 1차 가독성 개선은 범위를 좁게 유지했다.
9
+
10
+ - `readerSummary`로 Markdown final-report 앞부분에 사람이 먼저 읽을 요약 진입점을 둔다.
11
+ - HTML report view에 Reader Summary dashboard와 reader mode를 추가한다.
12
+ - 기존 `final-report` data는 `verdictCard` fallback으로 계속 호환한다.
13
+
14
+ 이 문서는 더 큰 3안을 현재 변경에 섞지 않고, 나중에 별도 판단할 재검토 후보로 남긴다.
15
+
16
+ ## 보류한 3안
17
+
18
+ `final-report` 산출물 자체를 목적별로 분리할지 재검토한다.
19
+
20
+ 현재 구조는 하나의 canonical Markdown 문서와 그 문서에서 생성되는 view를 유지한다. 3안은 요약 블록이나 HTML 필터링만으로 충분한지, 아니면 report를 목적별 artifact로 나누는 편이 더 나은지 비교하는 작업이다.
21
+
22
+ 비교 후보:
23
+
24
+ - 짧은 human handoff report와 별도 audit appendix.
25
+ - 같은 `data.json`에서 생성되는 `summary` view와 `audit` view.
26
+ - 사람이 먼저 행동하는 섹션만 남기는 phase별 slim report template.
27
+ - reporter handoff, implementer handoff, verifier audit, release handoff 같은 role별 view set.
28
+
29
+ ## 재검토 트리거
30
+
31
+ 아래 중 하나가 반복되면 이 문서를 다시 연다.
32
+
33
+ - `readerSummary`와 HTML reader mode 배포 후에도 사용자가 `final-report`를 읽기 어렵다고 말한다.
34
+ - 리뷰어가 항상 같은 audit 섹션을 건너뛰거나 더 작은 handoff artifact를 요구한다.
35
+ - `final-report` 생성에서 같은 evidence가 여러 섹션에 반복된다.
36
+ - 향후 schema migration에서 report artifact 계약을 어차피 건드려야 한다.
37
+
38
+ ## 결정할 질문
39
+
40
+ - Markdown이 계속 primary human artifact인가, 아니면 `data.json`만 canonical source로 두고 view를 생성할 것인가?
41
+ - follow-up task에 기본 첨부할 artifact는 무엇인가?
42
+ - 어떤 섹션이 human-action material이고, 어떤 섹션이 audit-only material인가?
43
+ - 분리를 validator가 강제해야 하는가, renderer convention으로 둘 것인가?
44
+ - 분리가 기존 `render-views`, follow-up spawning, approval sidecar에 어떤 영향을 주는가?
45
+
46
+ ## 참조
47
+
48
+ - 현재 schema: `schemas/final-report-v1.0.schema.json`
49
+ - 현재 Markdown template: `templates/reports/final-report.template.md`
50
+ - 현재 HTML renderer: `scripts/okstra_ctl/report_views.py`
51
+ - 현재 report UI assets: `templates/reports/report.css`, `templates/reports/report.js`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.114.0",
3
+ "version": "0.116.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.114.0",
3
- "builtAt": "2026-07-08T17:31:16.739Z",
2
+ "package": "0.116.0",
3
+ "builtAt": "2026-07-09T17:28:22.971Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -88,6 +88,7 @@ Rules (the schema enforces most of these — they are listed here so you know *w
88
88
  - `header.reportAuthor` is `"Report writer worker"`; `header.reportOwner` is `"Claude lead"`. Set author to `"Claude lead"` only for `release-handoff` runs (single-lead by design) or a recorded report-writer dispatch failure fallback.
89
89
  - **Source items (worker:item) preservation.** Every `consensus[].sourceItems`, `differences[].workersPosition[].itemId`, and `evidence.primary[].sourceItems` entry MUST carry the worker:item-id pair (e.g. `claude:F-001`, `codex:1.1`, `antigravity:F-3`, or `lead:mcp-1` for lead-only evidence). The schema enforces this via the `SourceItem` regex; bare worker-name lists no longer parse.
90
90
  - **Verdict Card consistency.** `verdictCard.verdictToken` / `.direction` / `.nextStep` MUST byte-match `finalVerdict.verdictToken` / `.direction` / `.nextStep` and `recommendedNextSteps[0].text`. The renderer pulls both from the same data structure — duplicating values across `verdictCard` and `finalVerdict` is intentional so the validator can diff them.
91
+ - **Reader Summary.** Populate `readerSummary` when the schema excerpt exposes it. It is the human-first entrypoint for both Markdown and HTML: one sentence for the decision, one for the human action required, one for blockers, one for audit sections safe to skip on first read, and one runnable recommended command. Do not duplicate raw evidence tables here.
91
92
  - **§7 phase-continuation row (mandatory for non-terminal task-types).** When `header.taskType` is one of `requirements-discovery` / `implementation-planning` / `error-analysis` / `implementation` / `final-verification`, `followUpTasks` MUST contain at least one row whose `origin` is `phase-continuation`, `suggestedTaskType` equals the next phase (byte-identical to `finalVerdict.nextStep`'s referenced phase), `newTaskId` reuses the current task-id, `autoSpawn` is `"no"`, and `priority` is `"P0"`. For `release-handoff` runs, omit the phase-continuation row. Schema `allOf` clause enforces this via `contains`.
92
93
  - **No deprecated sections.** The schema has no `4.5.8 User Approval Request` body field, no `4.5.9 Open Questions`, no `5.1 추가 자료 요청`, no `5.2 사용자 확인 질문` — clarifications go under the unified `clarificationItems[]` array.
93
94
  - **Optional Section 0.** Include `clarificationCarryIn` ONLY when the lead's prompt provides a non-empty carry-in path. Omit the key entirely otherwise (do NOT set it to `null` or an empty object).
@@ -85,3 +85,37 @@ Emit one `PROGRESS: <phase-id> <verb-phrase>` line as plain user-facing text at
85
85
  - Source path: `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}`
86
86
  - If the source path above is empty, no prior clarification response was attached to this run.
87
87
  - If the source path is set, a copy is staged at `{{INSTRUCTION_SET_RELATIVE_PATH}}/clarification-response.md`. Read it before running workers; reconcile each `C-*` row in section 1 (`## 1. Clarification Items`) of the prior report against new evidence and record the outcome in the conditional `## 0. Clarification Response Carried In From Previous Run` section of this run's final report (render that heading only when carry-in is non-empty — the validator fails empty Section 0 stubs).
88
+
89
+ ### Incremental re-verification (implementation-planning clarification re-runs only)
90
+
91
+ The **default is full re-verification**. Only narrow this re-run to the impacted stages when the deterministic `okstra incremental-scope` CLI returns `mode == "incremental"`; on any doubt, stay full. This procedure fires ONLY when this run's task-type is `implementation-planning` AND a prior final report exists for this task-key (its data.json at `runs/implementation-planning/reports/final-report-implementation-planning-<prev-seq>.data.json`, where `<prev-seq>` is the most recent prior implementation-planning run's seq). For every other task-type, ignore this block and re-verify normally. This branches on the CLI's `mode` output only — it does NOT re-implement the safety logic in the prompt.
92
+
93
+ 1. **Resolve the impacted stage set (safety condition C2 — your discretionary judgement).** For each answered `C-*` row you reconciled in §1, decide which **Stage Map stage numbers** (from the prior plan's `## 5.5 Stage Map`) the answer touches. Rules:
94
+ - Pass ONLY stage numbers that appear in that Stage Map. Never invent or guess a stage number — a number absent from the graph must never enter `--impacted` (it would leak into `reverify_stages`).
95
+ - If you cannot map a touched item to a real stage with confidence, leave the impacted set **EMPTY** — an empty set forces `mode == "full"`. Widening to full is always the safe choice; guessing is not.
96
+ - If any answer overturns the selected Option, restructures the stages, or changes the recommended approach (rather than a localized detail), also leave the impacted set **EMPTY**.
97
+ 2. **Resolve the two base SHAs (safety condition C1 — code-unchanged, decided by the CLI, not by you).**
98
+ - Current base SHA: `{{EXECUTOR_WORKTREE_BASE_REF}}` (this run's resolved worktree base commit).
99
+ - Prior base SHA: read the prior run's active-run-context at `runs/implementation-planning/state/active-run-context-implementation-planning-<prev-seq>.json`, field `executorWorktree.baseRef` (a resolved commit SHA). The run-manifest does NOT carry this field — use the active-run-context. If that file or field cannot be located, treat the run as **full** and skip the rest of this procedure.
100
+ 3. **Call the CLI** (it is pure — same inputs always yield the same decision):
101
+ ```
102
+ okstra incremental-scope \
103
+ --prev-data runs/implementation-planning/reports/final-report-implementation-planning-<prev-seq>.data.json \
104
+ --cur-base-sha {{EXECUTOR_WORKTREE_BASE_REF}} \
105
+ --prev-base-sha <prior baseRef from step 2> \
106
+ --impacted <csv of impacted stage numbers, empty for full>
107
+ ```
108
+ The CLI reads the plan's dependency graph from the top-level `## 5.5 Stage Map` (`implementationPlanning.stageMap`), which is authoritative for the impacted stage numbers — there is no per-option stage graph. The CLI prints JSON `{mode, reverify_stages, carry_stages, reason}`. Instruct the report-writer to record this JSON verbatim into this run's data.json as `implementationPlanning.incrementalDecision` (keys `mode`, `reverifyStages`, `carryStages`, `reason`) — the renderer turns it into the `### 0.1 Incremental Re-Verification Scope` audit block, and the validator fails an `incremental`-mode run whose Section 0 omits that block.
109
+ 4. **`mode == "full"`** → run the existing full re-verification path unchanged; ignore `reverify_stages` / `carry_stages`.
110
+ 5. **`mode == "incremental"`** → scope every worker dispatch prompt to `reverify_stages` only (the downstream closure of the impacted stages). Do NOT re-analyze `carry_stages` — their prior plan-item verdicts are carried forward verbatim (see `prompts/profiles/implementation-planning.md` "Cross-verification mode" and `prompts/lead/convergence.md` "Convergence scope").
111
+ 6. **Merge carried-forward verdicts.** In `incremental` mode, after this run's report-writer authors its data.json, instruct it to merge the prior plan-item verdicts for `carry_stages` into it:
112
+ ```
113
+ okstra incremental-carry \
114
+ --prev-data runs/implementation-planning/reports/final-report-implementation-planning-<prev-seq>.data.json \
115
+ --cur-data <this run's data.json> \
116
+ --prev-seq <prev-seq> \
117
+ --out <this run's data.json>
118
+ ```
119
+ A non-zero exit (`CarryError` — schema drift between the two runs) means the carry is unsafe: fall back to **full** — discard the incremental result and re-verify every stage. Note: `verdictCard` / `finalVerdict` are NEVER carried — this run re-computes them from the re-verified plus carried plan items.
120
+
121
+ **Carry completeness (BLOCKING).** In incremental mode, this run's `planItems` MUST contain every plan-item id from the re-verified stages, each carried forward with its updated verdict. If re-verification concludes a plan item should be REMOVED, that is a signal the answer's blast radius is NOT local — abandon incremental and re-route to a FULL re-verification. The carry merge only ever ADDS prior items whose id is absent from this run; it cannot distinguish a legitimate deletion from an untouched carry, so it would resurrect a stale verdict.
@@ -66,6 +66,8 @@ Read the worker result files generated in Phase 4/5 and extract individual findi
66
66
 
67
67
  **Convergence scope.** Convergence operates on sections 1–5 of the worker output (the common core, see `team-contract` "Worker Output Contract"). Section 6 ("Specialization Lens") is additive worker-specific depth and MUST NOT be fed into the consensus grouping, the verification queue, or the round-N reverify prompts. Carry Section 6 forward into the final report verbatim through the report-writer worker — do not let it inflate `unique` counts or trigger spurious `verification-error` statuses.
68
68
 
69
+ **Incremental re-verification scope (implementation-planning clarification re-runs).** When the lead's `okstra incremental-scope` decision is `mode == "incremental"` (procedure in `prompts/launch.template.md` §"Clarification Response Carried In"), only findings the lead attributes to a stage in `reverify_stages` enter the verification queue. Findings and plan-item verdicts carried forward for `carry_stages` are NOT re-queued — they skip the re-verification rounds entirely and are merged verbatim into this run's data.json via `okstra incremental-carry`. When the decision is `mode == "full"` (the default), every finding enters the queue as usual.
70
+
69
71
  1. In the "Findings" section of each worker's results, identify individual items by number (F-001, F-002, ...) and parse the ticket identifier attached to each item:
70
72
  - For table-form findings, read the `Ticket ID` column.
71
73
  - For bullet/numbered findings, parse `[TICKETID: <id>]` from the item title.
@@ -366,7 +366,7 @@ jq -s 'group_by(.errorType) | map({type: .[0].errorType, count: length})' <runDi
366
366
 
367
367
  The errors log is informational. Its presence/absence does not affect the final verdict. Do not block report writing on it.
368
368
 
369
- After persistence, reply briefly in the resolved Report Language with: completion status, final report path, team-state path, validator result, resume command path, any remaining blocker.
369
+ After persistence, reply briefly in the resolved Report Language with: completion status, final report path, team-state path, validator result, resume command path, any remaining blocker. **Lead this reply with the run's task identity** — state `<task-group>/<task-id>` (or the full `taskKey`) first, so the reader knows which task the reply is about. **Every run-artifact path in this reply MUST be task-qualified** — report the final report as `.okstra/tasks/<task-group>/<task-id>/runs/<task-type>/reports/final-report-<task-type>-<seq>.md` (the full path rooted at the task bundle), NOT the bare `runs/<task-type>/reports/...` form. The bare `runs/...` prefix is byte-for-byte identical across every task-group / task-id of the same task-type, so it cannot tell one task's report from another's — always emit the `.okstra/tasks/<task-group>/<task-id>/` prefix. The same task-qualified rule applies to the team-state path, resume command path, and any other run-artifact path this reply cites.
370
370
 
371
371
  ## Run-scoped pane & teammate lifecycle
372
372
 
@@ -281,7 +281,7 @@ Every field MUST anchor its claim with at least one evidence reference — a `pa
281
281
 
282
282
  **Reader-facing prose MUST NOT cite a bare brief/worker-internal ID that this report never surfaces** — `RC-*` (reporter confirmations, defined in the brief), `RF-*` / `F-*` (findings, defined in worker-results) have no anchor in the final report, so a reader hits an opaque token with nothing to click. Either expand it inline (`the confirmed version target 1.27.47→1.27.48`) or, for an audit trail, namespace it (`claude:F-005`). **Enforced:** `_validate_no_opaque_id_references` fails a bare `RC-*` / `RF-*` / non-namespaced `F-*` appearing in `verdictCard` / `finalVerdict` / `rationale` / `clarificationItems[].statement`. (The renderer anchors + links in-report IDs of any digit width, so a surfaced `RC-4`-style id does resolve.) Do NOT restate the Verdict Card or the §5.4 trade-off matrix verbatim — this section is the *why*, in connected prose, that those tables compress.
283
283
 
284
- 0. **Clarification Response Carried In** — render this `## 0.` heading ONLY when `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}` is non-empty. Walk every `C-*` row of the prior report's `## 1. Clarification Items` table, reconcile against new evidence, and record the outcome (`resolved` / `obsolete`) with citation before drafting the verdict. When no carry-in path was provided, OMIT the `## 0.` heading entirely — the validator fails an empty Section 0 stub.
284
+ 0. **Clarification Response Carried In** — render this `## 0.` heading ONLY when `{{CLARIFICATION_RESPONSE_RELATIVE_PATH}}` is non-empty. Walk every `C-*` row of the prior report's `## 1. Clarification Items` table, reconcile against new evidence, and record the outcome (`resolved` / `obsolete`) with citation before drafting the verdict. When no carry-in path was provided, OMIT the `## 0.` heading entirely — the validator fails an empty Section 0 stub. When the lead ran `okstra incremental-scope` for this re-run, record its JSON verbatim into `implementationPlanning.incrementalDecision` (`mode`, `reverifyStages`, `carryStages`, `reason`); the renderer emits the `### 0.1 Incremental Re-Verification Scope` audit block from it, and the validator fails an `incremental`-mode run whose Section 0 omits that block. In `incremental` mode this run's `planItems` MUST carry every plan-item id from the re-verified stages forward with its updated verdict; if re-verification concludes a plan item should be REMOVED, that is a signal the answer's blast radius is not local — do not drop it here, tell the lead to abandon incremental and re-route to a FULL re-verification, because the carry merge only adds prior items and would resurrect the removed item's stale verdict.
285
285
  1. **Cross Verification Results** — 4 categories (Full / Partial / Contested / Worker-Unique) when convergence is enabled, per `convergence`. Prepend the Round History sub-table (columns: `Round | inputQueueSize | resolvedCount | carriedForwardCount | dispatches | skippedWorkers`) plus a `round2SkippedReason: <value>` note, pulled verbatim from `convergence-<task-type>-<seq>.json`. Empty contested list renders as `- 합의 미달 항목 없음.`. Convergence-disabled runs use the legacy Consensus/Differences format and omit the round table.
286
286
  2. **Final Verdict** — `Direction` ∈ `continue-investigation` / `begin-implementation` / `approve` / `reject` / `hold`. **Verdict Token** is `not-applicable` for every task-type except `final-verification` — see "Final-verification verdict token contract" below for that case.
287
287
  3. **Evidence and Detailed Analysis** — primary evidence rows (file path, line, snippet); secondary evidence / alternate interpretations. If `reference-expectations.md` lists explicit expected values, record match/gap per row.
@@ -43,6 +43,7 @@
43
43
  - Cross-verification mode:
44
44
  - Phase 5.5 finding convergence runs in **adversarial mode** for this phase (`convergence.adversarial=true`). Verifiers actively try to refute each worker finding (requirement gap / risk / option) by re-inspecting its cited evidence; the burden of proof sits on the claim. See `prompts/lead/convergence.md` §"Adversarial Verification Mode".
45
45
  - §5.5.9 plan-body verification runs with an **adversarial posture** (`prompts/lead/plan-body-verification.md` §"Adversarial plan-body posture"): verifiers open and confirm every cited path / command and put the burden of proof on the plan. The gate threshold is majority-based for kinds `b`/`c`/`e`, but a single `DISAGREE` blocks on its own for the concrete, safety-critical kinds `a` (path/symbol mismatch) / `d` (rollback order) — and `f` on `P-Req-*` items. A majority also needs ≥2 participating votes, so a lone dissent whose peer returned a non-result does not block on a majority-gated kind (see that contract's §"Adversarial plan-body posture").
46
+ - **Incremental re-verification scope (clarification re-runs):** when the lead's `okstra incremental-scope` decision is `mode == "incremental"` (procedure in `prompts/launch.template.md` §"Clarification Response Carried In"), workers re-analyze ONLY the stages listed in `reverify_stages` (the downstream closure of the impacted stages). Workers MUST NOT re-open, re-score, or re-judge any stage in `carry_stages` — those stages' prior plan-item verdicts are carried forward verbatim, and a worker never overwrites a carried verdict with its own judgement. When the decision is `mode == "full"` (the default), every stage is re-analyzed as usual.
46
47
  {{INCLUDE:_coverage-critic.md}}
47
48
  - Non-goals:
48
49
  - code-level micro-optimization unless it changes the implementation approach
@@ -1,8 +1,8 @@
1
1
  """Implementation stage run orchestration.
2
2
 
3
- This module owns the lifecycle for one ``implementation`` stage run: recover
4
- consumer state, select an available Stage Map entry, provision the isolated
5
- stage worktree, and publish the selected stage into run context.
3
+ This module owns the Stage Run Claim for one ``implementation`` stage run:
4
+ recover consumer state, select an available Stage Map entry, provision the
5
+ isolated stage worktree, and record the started event.
6
6
  """
7
7
  from __future__ import annotations
8
8
 
@@ -18,12 +18,12 @@ from .stage_reconcile import auto_reconcile_best_effort
18
18
 
19
19
 
20
20
  class ImplementationStageError(Exception):
21
- """Implementation stage selection or provisioning failed."""
21
+ """Implementation stage claim or provisioning failed."""
22
22
 
23
23
 
24
24
  @dataclass
25
- class StageSelection:
26
- """Resolved implementation stage and its isolated worktree coordinates."""
25
+ class StageRunClaim:
26
+ """Claim over one Stage Map stage and its isolated worktree coordinates."""
27
27
 
28
28
  stage: int
29
29
  worktree_path: str
@@ -50,22 +50,20 @@ def _as_implementation_stage_error(exc: Exception) -> ImplementationStageError:
50
50
  return ImplementationStageError(str(exc))
51
51
 
52
52
 
53
- def select_and_provision_implementation_stage(
53
+ def claim_implementation_stage_run(
54
54
  inp: Any,
55
55
  ctx_stage_map: list[dict[str, Any]],
56
56
  task_group_segment: str,
57
57
  task_id_segment: str,
58
58
  task_key: str,
59
59
  executor_worktree_status: str,
60
- ) -> StageSelection:
61
- """Select a ready implementation stage and provision its stage worktree.
60
+ ) -> StageRunClaim:
61
+ """Claim a ready implementation stage for one run.
62
62
 
63
- The returned selection is path-independent with respect to run artifacts:
63
+ The returned claim is path-independent with respect to run artifacts:
64
64
  callers can compute stage-specific run paths after this function resolves
65
65
  the selected stage.
66
66
  """
67
- del task_key # The registry uses the split identity fields.
68
-
69
67
  from .consumers import backfill_done_from_carry
70
68
  from . import worktree as _worktree
71
69
  from . import worktree_registry as _reg
@@ -95,7 +93,7 @@ def select_and_provision_implementation_stage(
95
93
 
96
94
  if executor_worktree_status.startswith("skipped"):
97
95
  head = _git_out(inp.project_root, "rev-parse", "HEAD")
98
- return StageSelection(
96
+ claim = StageRunClaim(
99
97
  stage=selected,
100
98
  worktree_path="",
101
99
  worktree_branch="",
@@ -105,6 +103,8 @@ def select_and_provision_implementation_stage(
105
103
  started_head_commit=head,
106
104
  concurrent_stages=concurrent_stages,
107
105
  )
106
+ _record_stage_run_claim_started(task_key, plan_run_root, claim)
107
+ return claim
108
108
 
109
109
  # The anchor and multi-dep candidate base must come from the task-key
110
110
  # worktree HEAD (where stage work accumulates), not from inp.project_root
@@ -166,7 +166,7 @@ def select_and_provision_implementation_stage(
166
166
  f"stage worktree provisioning failed: {exc}\n{hint}"
167
167
  ) from exc
168
168
 
169
- return StageSelection(
169
+ claim = StageRunClaim(
170
170
  stage=selected,
171
171
  worktree_path=prov.path,
172
172
  worktree_branch=prov.branch,
@@ -176,22 +176,40 @@ def select_and_provision_implementation_stage(
176
176
  started_head_commit=prov.base_ref,
177
177
  concurrent_stages=concurrent_stages,
178
178
  )
179
+ _record_stage_run_claim_started(task_key, plan_run_root, claim)
180
+ return claim
181
+
182
+
183
+ def _record_stage_run_claim_started(
184
+ task_key: str,
185
+ plan_run_root: Path,
186
+ claim: StageRunClaim,
187
+ ) -> None:
188
+ from .consumers import append_consumer
179
189
 
190
+ now = _dt.datetime.now(_dt.timezone.utc).isoformat()
191
+ append_consumer(
192
+ plan_run_root,
193
+ impl_task_key=task_key,
194
+ stage=claim.stage,
195
+ status="started",
196
+ started_at=now,
197
+ head_commit=claim.started_head_commit,
198
+ )
180
199
 
181
- def apply_implementation_stage(
200
+
201
+ def publish_stage_run_claim(
182
202
  inp: Any,
183
203
  ctx: dict[str, Any],
184
204
  ctx_stage_map: list[dict[str, Any]],
185
- sel: StageSelection,
205
+ claim: StageRunClaim,
186
206
  ) -> None:
187
- """Publish a selected stage into run context and ``consumers.jsonl``."""
188
- from .consumers import append_consumer
189
-
207
+ """Publish a Stage Run Claim into run context."""
190
208
  ctx["parsed_stage_map"] = ctx_stage_map
191
- ctx["effective_stages"] = [sel.stage]
192
- csv = str(sel.stage)
209
+ ctx["effective_stages"] = [claim.stage]
210
+ csv = str(claim.stage)
193
211
  ctx["EFFECTIVE_STAGES"] = csv
194
- ctx["CONCURRENT_RUN_STAGES"] = ",".join(str(s) for s in sel.concurrent_stages)
212
+ ctx["CONCURRENT_RUN_STAGES"] = ",".join(str(s) for s in claim.concurrent_stages)
195
213
  ctx["STAGE_BATCH_DIRECTIVE"] = (
196
214
  f"- **Stage for this implementation run:** `{csv}`. "
197
215
  "Execute exactly this Stage Map stage — this is the authoritative scope. "
@@ -201,20 +219,9 @@ def apply_implementation_stage(
201
219
  inp.stage = csv
202
220
  print(f"selected stages: {csv}", file=sys.stdout)
203
221
 
204
- if sel.worktree_status and not sel.worktree_status.startswith("skipped"):
205
- ctx["EXECUTOR_WORKTREE_PATH"] = sel.worktree_path
206
- ctx["EXECUTOR_WORKTREE_BRANCH"] = sel.worktree_branch
207
- ctx["EXECUTOR_WORKTREE_BASE_REF"] = sel.worktree_base_ref
208
- ctx["EXECUTOR_WORKTREE_STATUS"] = sel.worktree_status
209
- ctx["EXECUTOR_WORKTREE_NOTE"] = sel.worktree_note
210
-
211
- now = _dt.datetime.now(_dt.timezone.utc).isoformat()
212
- plan_run_root = Path(inp.approved_plan_path).resolve().parents[1]
213
- append_consumer(
214
- plan_run_root,
215
- impl_task_key=ctx["TASK_KEY"],
216
- stage=sel.stage,
217
- status="started",
218
- started_at=now,
219
- head_commit=sel.started_head_commit,
220
- )
222
+ if claim.worktree_status and not claim.worktree_status.startswith("skipped"):
223
+ ctx["EXECUTOR_WORKTREE_PATH"] = claim.worktree_path
224
+ ctx["EXECUTOR_WORKTREE_BRANCH"] = claim.worktree_branch
225
+ ctx["EXECUTOR_WORKTREE_BASE_REF"] = claim.worktree_base_ref
226
+ ctx["EXECUTOR_WORKTREE_STATUS"] = claim.worktree_status
227
+ ctx["EXECUTOR_WORKTREE_NOTE"] = claim.worktree_note
@@ -0,0 +1,70 @@
1
+ """Merge carried-forward plan-item verdicts from the prior run into the
2
+ current incremental re-run's data.json. Plan items the current run did not
3
+ re-verify keep their prior verdict, tagged so the report shows they are
4
+ carried forward and unchanged.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import argparse
9
+ import json
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ class CarryError(Exception):
15
+ """Carry merge refused — schema drift or structural mismatch."""
16
+
17
+
18
+ def merge_carried_forward(prev: dict, cur: dict, prev_seq: str = "previous") -> dict:
19
+ if prev.get("schemaVersion") != cur.get("schemaVersion"):
20
+ raise CarryError(
21
+ f"schemaVersion drift {prev.get('schemaVersion')!r} != {cur.get('schemaVersion')!r}; "
22
+ "cannot carry forward — caller must fall back to full"
23
+ )
24
+ prev_pbv = prev.get("implementationPlanning", {}).get("planBodyVerification", {})
25
+ cur_ip = cur.setdefault("implementationPlanning", {})
26
+ cur_pbv = cur_ip.setdefault("planBodyVerification", {})
27
+ cur_items = cur_pbv.setdefault("planItems", [])
28
+ reverified_ids = {i["id"] for i in cur_items}
29
+ for item in prev_pbv.get("planItems", []):
30
+ if item["id"] in reverified_ids:
31
+ continue
32
+ carried = dict(item)
33
+ carried["carriedForwardFromSeq"] = str(prev_seq)
34
+ cur_items.append(carried)
35
+ cur_items.sort(key=lambda i: i["id"])
36
+ return cur
37
+
38
+
39
+ def main(argv: list[str]) -> int:
40
+ ap = argparse.ArgumentParser(prog="okstra incremental-carry")
41
+ ap.add_argument("--prev-data", required=True, help="prior run final-report data.json")
42
+ ap.add_argument("--cur-data", required=True, help="current incremental re-run data.json")
43
+ ap.add_argument("--prev-seq", required=True, help="prior run seq, tagged onto carried items")
44
+ ap.add_argument("--out", required=True, help="path to write the merged data.json")
45
+ args = ap.parse_args(argv)
46
+
47
+ prev = json.loads(Path(args.prev_data).read_text(encoding="utf-8"))
48
+ cur = json.loads(Path(args.cur_data).read_text(encoding="utf-8"))
49
+ try:
50
+ merged = merge_carried_forward(prev, cur, prev_seq=args.prev_seq)
51
+ except CarryError as exc:
52
+ print(f"carry refused: {exc}", file=sys.stderr)
53
+ return 1
54
+
55
+ Path(args.out).write_text(
56
+ json.dumps(merged, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
57
+ )
58
+ carried = sum(
59
+ 1
60
+ for i in merged.get("implementationPlanning", {})
61
+ .get("planBodyVerification", {})
62
+ .get("planItems", [])
63
+ if i.get("carriedForwardFromSeq") == str(args.prev_seq)
64
+ )
65
+ print(f"merged {carried} carried-forward plan item(s) from seq {args.prev_seq} -> {args.out}")
66
+ return 0
67
+
68
+
69
+ if __name__ == "__main__":
70
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,89 @@
1
+ """Decide whether an implementation-planning clarification re-run can be
2
+ incremental, and if so which stages to re-verify vs carry forward.
3
+
4
+ Deterministic half of the incremental-reverification feature: the lead
5
+ prompt supplies the impacted-stage set (its discretionary C2 judgement) and
6
+ the base SHAs; everything here is pure so the same inputs always yield the
7
+ same decision. See docs/superpowers/specs/2026-07-09-incremental-clarification-reverification-design.md.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+ import sys
14
+ from dataclasses import asdict, dataclass
15
+ from pathlib import Path
16
+
17
+ from okstra_ctl.stage_targets import downstream_stage_closure
18
+
19
+ CUTOFF_RATIO = 0.5
20
+
21
+
22
+ @dataclass
23
+ class IncrementalDecision:
24
+ mode: str # "incremental" | "full"
25
+ reverify_stages: list[int]
26
+ carry_stages: list[int]
27
+ reason: str
28
+
29
+
30
+ def _parse_depends_on(cell: str) -> list[int]:
31
+ text = (cell or "").strip()
32
+ if not text or text == "(none)":
33
+ return []
34
+ return [int(tok.strip()) for tok in text.split(",") if tok.strip()]
35
+
36
+
37
+ def parse_stage_graph(data: dict) -> list[tuple[int, list[int]]]:
38
+ stage_map = data.get("implementationPlanning", {}).get("stageMap", [])
39
+ return [(int(row["stage"]), _parse_depends_on(row.get("dependsOn", ""))) for row in stage_map]
40
+
41
+
42
+ def decide_scope(
43
+ *,
44
+ stages: list[tuple[int, list[int]]],
45
+ impacted_stages: set[int],
46
+ prev_base_sha: str,
47
+ cur_base_sha: str,
48
+ cutoff_ratio: float = CUTOFF_RATIO,
49
+ ) -> IncrementalDecision:
50
+ if not prev_base_sha or prev_base_sha != cur_base_sha:
51
+ return IncrementalDecision(
52
+ "full", [], [],
53
+ f"base-ref changed ({prev_base_sha!r} -> {cur_base_sha!r}); prior analysis invalid",
54
+ )
55
+ if not impacted_stages:
56
+ return IncrementalDecision("full", [], [], "no impacted stages resolved; falling back to full")
57
+ all_stages = {num for num, _ in stages}
58
+ closure = downstream_stage_closure(stages, set(impacted_stages))
59
+ if len(closure) * 2 > len(all_stages):
60
+ return IncrementalDecision(
61
+ "full", [], [],
62
+ f"closure {len(closure)}/{len(all_stages)} exceeds {int(cutoff_ratio * 100)}% cutoff",
63
+ )
64
+ reverify = sorted(closure)
65
+ carry = sorted(all_stages - closure)
66
+ return IncrementalDecision("incremental", reverify, carry, f"closure {reverify} within cutoff")
67
+
68
+
69
+ def main(argv: list[str]) -> int:
70
+ ap = argparse.ArgumentParser(prog="okstra incremental-scope")
71
+ ap.add_argument("--prev-data", required=True, help="prior run final-report data.json")
72
+ ap.add_argument("--cur-base-sha", required=True)
73
+ ap.add_argument("--prev-base-sha", required=True)
74
+ ap.add_argument("--impacted", default="", help="comma-separated impacted stage numbers")
75
+ args = ap.parse_args(argv)
76
+
77
+ data = json.loads(Path(args.prev_data).read_text(encoding="utf-8"))
78
+ stages = parse_stage_graph(data)
79
+ impacted = {int(t.strip()) for t in args.impacted.split(",") if t.strip()}
80
+ decision = decide_scope(
81
+ stages=stages, impacted_stages=impacted,
82
+ prev_base_sha=args.prev_base_sha, cur_base_sha=args.cur_base_sha,
83
+ )
84
+ print(json.dumps(asdict(decision), ensure_ascii=False))
85
+ return 0
86
+
87
+
88
+ if __name__ == "__main__":
89
+ raise SystemExit(main(sys.argv[1:]))