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.
- package/docs/kr/architecture.md +2 -2
- package/docs/kr/follow-ups/2026-07-10-final-report-option-3.md +51 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +1 -0
- package/runtime/prompts/launch.template.md +34 -0
- package/runtime/prompts/lead/convergence.md +2 -0
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +1 -0
- package/runtime/python/okstra_ctl/implementation_stage.py +46 -39
- package/runtime/python/okstra_ctl/incremental_carry.py +70 -0
- package/runtime/python/okstra_ctl/incremental_scope.py +89 -0
- package/runtime/python/okstra_ctl/report_views.py +151 -12
- package/runtime/python/okstra_ctl/run.py +24 -24
- package/runtime/python/okstra_ctl/stage_targets.py +26 -0
- package/runtime/python/okstra_ctl/worktree.py +1 -1
- package/runtime/python/okstra_token_usage/claude.py +58 -19
- package/runtime/python/okstra_token_usage/collect.py +3 -1
- package/runtime/python/okstra_vendor/__init__.py +3 -0
- package/runtime/schemas/final-report-v1.0.schema.json +39 -0
- package/runtime/templates/reports/final-report.template.md +27 -0
- package/runtime/templates/reports/i18n/en.json +23 -12
- package/runtime/templates/reports/i18n/ko.json +23 -12
- package/runtime/templates/reports/report.css +55 -0
- package/runtime/templates/reports/report.js +53 -1
- package/runtime/validators/validate-run.py +50 -0
- package/src/cli-registry.mjs +14 -0
- package/src/commands/execute/incremental-carry.mjs +20 -0
- package/src/commands/execute/incremental-scope.mjs +20 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_approx_clust_coeff.py +0 -41
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_clique.py +0 -112
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_connectivity.py +0 -199
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_density.py +0 -146
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_distance_measures.py +0 -59
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_dominating_set.py +0 -78
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_kcomponents.py +0 -303
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_matching.py +0 -8
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_maxcut.py +0 -94
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_ramsey.py +0 -31
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_steinertree.py +0 -306
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_traveling_salesman.py +0 -1014
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_treewidth.py +0 -274
- package/runtime/python/okstra_vendor/networkx/algorithms/approximation/tests/test_vertex_cover.py +0 -68
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/base_test.py +0 -81
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_connectivity.py +0 -143
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_correlation.py +0 -122
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_mixing.py +0 -174
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_neighbor_degree.py +0 -107
- package/runtime/python/okstra_vendor/networkx/algorithms/assortativity/tests/test_pairs.py +0 -87
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_basic.py +0 -125
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_centrality.py +0 -192
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_cluster.py +0 -84
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_covering.py +0 -33
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_edgelist.py +0 -240
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_extendability.py +0 -334
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_generators.py +0 -407
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_link_analysis.py +0 -218
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matching.py +0 -327
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_matrix.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_project.py +0 -409
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_redundancy.py +0 -35
- package/runtime/python/okstra_vendor/networkx/algorithms/bipartite/tests/test_spectral_bipartivity.py +0 -80
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality.py +0 -923
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_betweenness_centrality_subset.py +0 -354
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_closeness_centrality.py +0 -274
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality.py +0 -259
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_betweenness_centrality_subset.py +0 -147
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_current_flow_closeness.py +0 -43
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_degree_centrality.py +0 -144
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_dispersion.py +0 -73
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_eigenvector_centrality.py +0 -186
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_group.py +0 -277
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_harmonic_centrality.py +0 -122
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_katz_centrality.py +0 -345
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_laplacian_centrality.py +0 -220
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_load_centrality.py +0 -344
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_percolation_centrality.py +0 -87
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_reaching.py +0 -140
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_second_order_centrality.py +0 -82
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_subgraph.py +0 -110
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_trophic.py +0 -302
- package/runtime/python/okstra_vendor/networkx/algorithms/centrality/tests/test_voterank.py +0 -64
- package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/coloring/tests/test_coloring.py +0 -863
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_asyn_fluid.py +0 -147
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_bipartitions.py +0 -157
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_centrality.py +0 -85
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_divisive.py +0 -106
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_kclique.py +0 -91
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_label_propagation.py +0 -241
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_leiden.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_local.py +0 -76
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_louvain.py +0 -264
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_lukes.py +0 -152
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_modularity_max.py +0 -340
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_quality.py +0 -139
- package/runtime/python/okstra_vendor/networkx/algorithms/community/tests/test_utils.py +0 -26
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_attracting.py +0 -70
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_biconnected.py +0 -248
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_connected.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_semiconnected.py +0 -55
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_strongly_connected.py +0 -193
- package/runtime/python/okstra_vendor/networkx/algorithms/components/tests/test_weakly_connected.py +0 -96
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_connectivity.py +0 -421
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_cuts.py +0 -309
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_disjoint_paths.py +0 -249
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_augmentation.py +0 -502
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_edge_kcomponents.py +0 -488
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcomponents.py +0 -323
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_kcutsets.py +0 -280
- package/runtime/python/okstra_vendor/networkx/algorithms/connectivity/tests/test_stoer_wagner.py +0 -102
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gl1.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/gw1.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/netgen-2.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_gomory_hu.py +0 -128
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow.py +0 -573
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_maxflow_large_graph.py +0 -155
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_mincost.py +0 -475
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/test_networksimplex.py +0 -481
- package/runtime/python/okstra_vendor/networkx/algorithms/flow/tests/wlm3.gpickle.bz2 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.A99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/iso_r01_s80.B99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.A99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/si2_b06_m200.B99 +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_ismags.py +0 -719
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphism.py +0 -103
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_isomorphvf2.py +0 -490
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_match_helpers.py +0 -64
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_temporalisomorphvf2.py +0 -212
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_tree_isomorphism.py +0 -202
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp.py +0 -1655
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2pp_helpers.py +0 -3118
- package/runtime/python/okstra_vendor/networkx/algorithms/isomorphism/tests/test_vf2userfunc.py +0 -196
- package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_hits.py +0 -77
- package/runtime/python/okstra_vendor/networkx/algorithms/link_analysis/tests/test_pagerank.py +0 -213
- package/runtime/python/okstra_vendor/networkx/algorithms/minors/tests/test_contraction.py +0 -544
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_all.py +0 -328
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_binary.py +0 -451
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_product.py +0 -491
- package/runtime/python/okstra_vendor/networkx/algorithms/operators/tests/test_unary.py +0 -55
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_astar.py +0 -254
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense.py +0 -212
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py +0 -88
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_generic.py +0 -511
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_unweighted.py +0 -149
- package/runtime/python/okstra_vendor/networkx/algorithms/shortest_paths/tests/test_weighted.py +0 -983
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_asteroidal.py +0 -23
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_boundary.py +0 -154
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_bridges.py +0 -144
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_broadcasting.py +0 -109
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chains.py +0 -136
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_chordal.py +0 -129
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_clique.py +0 -300
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cluster.py +0 -678
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_communicability.py +0 -80
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_core.py +0 -266
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_covering.py +0 -85
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cuts.py +0 -171
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_cycles.py +0 -984
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_d_separation.py +0 -340
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dag.py +0 -835
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_measures.py +0 -831
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_distance_regular.py +0 -85
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominance.py +0 -299
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_dominating.py +0 -115
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_efficiency.py +0 -58
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_euler.py +0 -314
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graph_hashing.py +0 -872
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_graphical.py +0 -163
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hierarchy.py +0 -46
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_hybrid.py +0 -24
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_isolate.py +0 -26
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_link_prediction.py +0 -615
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_lowest_common_ancestors.py +0 -459
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_matching.py +0 -556
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_max_weight_clique.py +0 -179
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_mis.py +0 -62
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_moral.py +0 -15
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_node_classification.py +0 -140
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_non_randomness.py +0 -60
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_perfect_graph.py +0 -27
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planar_drawing.py +0 -274
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_planarity.py +0 -556
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_polynomials.py +0 -57
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_reciprocity.py +0 -37
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_regular.py +0 -88
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_richclub.py +0 -149
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_similarity.py +0 -1158
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_simple_paths.py +0 -803
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smallworld.py +0 -76
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_smetric.py +0 -8
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_sparsifiers.py +0 -138
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_structuralholes.py +0 -191
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_summarization.py +0 -642
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_swap.py +0 -179
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_threshold.py +0 -270
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_time_dependent.py +0 -431
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_tournament.py +0 -161
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_triads.py +0 -248
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_vitality.py +0 -41
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_voronoi.py +0 -103
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_walks.py +0 -54
- package/runtime/python/okstra_vendor/networkx/algorithms/tests/test_wiener.py +0 -157
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_beamsearch.py +0 -25
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_bfs.py +0 -203
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_dfs.py +0 -307
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgebfs.py +0 -147
- package/runtime/python/okstra_vendor/networkx/algorithms/traversal/tests/test_edgedfs.py +0 -131
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_branchings.py +0 -624
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_coding.py +0 -114
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_decomposition.py +0 -79
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_distance_measures.py +0 -99
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_mst.py +0 -934
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_operations.py +0 -53
- package/runtime/python/okstra_vendor/networkx/algorithms/tree/tests/test_recognition.py +0 -174
- package/runtime/python/okstra_vendor/networkx/classes/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/classes/tests/dispatch_interface.py +0 -192
- package/runtime/python/okstra_vendor/networkx/classes/tests/historical_tests.py +0 -476
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_coreviews.py +0 -362
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph.py +0 -331
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_digraph_historical.py +0 -110
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_filters.py +0 -177
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_function.py +0 -1045
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph.py +0 -950
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_graph_historical.py +0 -12
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_graphviews.py +0 -349
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_multidigraph.py +0 -459
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_multigraph.py +0 -528
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_reportviews.py +0 -1421
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_special.py +0 -131
- package/runtime/python/okstra_vendor/networkx/classes/tests/test_subgraphviews.py +0 -371
- package/runtime/python/okstra_vendor/networkx/conftest.py +0 -261
- package/runtime/python/okstra_vendor/networkx/drawing/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_complex.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_empty_graph.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_house_with_colors.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_labels_and_colors.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_display_shortest_path.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/baseline/test_house_with_colors.png +0 -0
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_agraph.py +0 -237
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_image_comparison_pylab_mpl.py +0 -229
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_latex.py +0 -285
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_layout.py +0 -631
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pydot.py +0 -146
- package/runtime/python/okstra_vendor/networkx/drawing/tests/test_pylab.py +0 -1582
- package/runtime/python/okstra_vendor/networkx/generators/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_atlas.py +0 -75
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_classic.py +0 -642
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_cographs.py +0 -20
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_community.py +0 -362
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_degree_seq.py +0 -224
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_directed.py +0 -189
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_duplication.py +0 -103
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_ego.py +0 -39
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_expanders.py +0 -182
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_geometric.py +0 -488
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_harary_graph.py +0 -133
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_internet_as_graphs.py +0 -221
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_intersection.py +0 -28
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_interval_graph.py +0 -144
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_joint_degree_seq.py +0 -125
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_lattice.py +0 -264
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_line.py +0 -316
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_mycielski.py +0 -30
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_nonisomorphic_trees.py +0 -82
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_clustered.py +0 -33
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_random_graphs.py +0 -495
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_small.py +0 -220
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_spectral_graph_forge.py +0 -49
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_stochastic.py +0 -72
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_sudoku.py +0 -92
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_time_series.py +0 -64
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_trees.py +0 -195
- package/runtime/python/okstra_vendor/networkx/generators/tests/test_triads.py +0 -15
- package/runtime/python/okstra_vendor/networkx/linalg/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_algebraic_connectivity.py +0 -400
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_attrmatrix.py +0 -108
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_bethehessian.py +0 -40
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_graphmatrix.py +0 -275
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_laplacian.py +0 -334
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_modularity.py +0 -86
- package/runtime/python/okstra_vendor/networkx/linalg/tests/test_spectrum.py +0 -70
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_adjacency.py +0 -78
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_cytoscape.py +0 -78
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_node_link.py +0 -109
- package/runtime/python/okstra_vendor/networkx/readwrite/json_graph/tests/test_tree.py +0 -48
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_adjlist.py +0 -354
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_edgelist.py +0 -318
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gexf.py +0 -612
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_gml.py +0 -744
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graph6.py +0 -181
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_graphml.py +0 -1531
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_leda.py +0 -30
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_p2g.py +0 -63
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_pajek.py +0 -128
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_sparse6.py +0 -166
- package/runtime/python/okstra_vendor/networkx/readwrite/tests/test_text.py +0 -1742
- package/runtime/python/okstra_vendor/networkx/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/tests/test_all_random_functions.py +0 -248
- package/runtime/python/okstra_vendor/networkx/tests/test_convert.py +0 -321
- package/runtime/python/okstra_vendor/networkx/tests/test_convert_numpy.py +0 -531
- package/runtime/python/okstra_vendor/networkx/tests/test_convert_pandas.py +0 -349
- package/runtime/python/okstra_vendor/networkx/tests/test_convert_scipy.py +0 -281
- package/runtime/python/okstra_vendor/networkx/tests/test_exceptions.py +0 -40
- package/runtime/python/okstra_vendor/networkx/tests/test_import.py +0 -11
- package/runtime/python/okstra_vendor/networkx/tests/test_lazy_imports.py +0 -96
- package/runtime/python/okstra_vendor/networkx/tests/test_relabel.py +0 -349
- package/runtime/python/okstra_vendor/networkx/tests/test_removed_functions_exception_messages.py +0 -8
- package/runtime/python/okstra_vendor/networkx/utils/tests/__init__.py +0 -0
- package/runtime/python/okstra_vendor/networkx/utils/tests/test__init.py +0 -11
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_backends.py +0 -225
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_config.py +0 -263
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_decorators.py +0 -510
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_heaps.py +0 -131
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_mapped_queue.py +0 -268
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_misc.py +0 -393
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_random_sequence.py +0 -53
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_rcm.py +0 -63
- package/runtime/python/okstra_vendor/networkx/utils/tests/test_unionfind.py +0 -55
|
@@ -67,6 +67,7 @@ def _strip_leading_frontmatter(text: str) -> str:
|
|
|
67
67
|
|
|
68
68
|
from .clarification_items import (
|
|
69
69
|
_CELL_ANCHOR_RE,
|
|
70
|
+
UNRESOLVED_STATUSES,
|
|
70
71
|
_section_1_slice,
|
|
71
72
|
_split_pipe_row,
|
|
72
73
|
parse_clarification_items,
|
|
@@ -117,6 +118,19 @@ class ReportViewModel:
|
|
|
117
118
|
sidecar_name: str
|
|
118
119
|
sidecar_dir: str
|
|
119
120
|
approval_ctx: PlanApprovalContext | None = None
|
|
121
|
+
reader_ctx: ReaderDashboardContext | None = None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass(frozen=True)
|
|
125
|
+
class ReaderDashboardContext:
|
|
126
|
+
decision: str
|
|
127
|
+
human_action_required: str
|
|
128
|
+
blocking_items: str
|
|
129
|
+
safe_to_skip: str
|
|
130
|
+
recommended_command: str
|
|
131
|
+
open_clarifications: int
|
|
132
|
+
approval: str
|
|
133
|
+
recommended_option: str
|
|
120
134
|
|
|
121
135
|
|
|
122
136
|
def build_report_view_model(
|
|
@@ -124,6 +138,7 @@ def build_report_view_model(
|
|
|
124
138
|
*,
|
|
125
139
|
run_meta: RunMeta,
|
|
126
140
|
approval_ctx: PlanApprovalContext | None = None,
|
|
141
|
+
reader_ctx: ReaderDashboardContext | None = None,
|
|
127
142
|
) -> ReportViewModel:
|
|
128
143
|
digest = source_digest(src_md)
|
|
129
144
|
body_md = _strip_leading_frontmatter(src_md)
|
|
@@ -141,6 +156,36 @@ def build_report_view_model(
|
|
|
141
156
|
sidecar_name=f"user-response-{run_meta.task_type}-{run_meta.seq}.md",
|
|
142
157
|
sidecar_dir=f"runs/{run_meta.task_type}/user-responses/",
|
|
143
158
|
approval_ctx=approval_ctx,
|
|
159
|
+
reader_ctx=reader_ctx,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _reader_dashboard(ctx: ReaderDashboardContext | None) -> str:
|
|
164
|
+
if ctx is None:
|
|
165
|
+
return ""
|
|
166
|
+
rows = (
|
|
167
|
+
("Decision", html.escape(ctx.decision)),
|
|
168
|
+
("Human action required", html.escape(ctx.human_action_required)),
|
|
169
|
+
("Blocking items", html.escape(ctx.blocking_items)),
|
|
170
|
+
("Safe to skip", html.escape(ctx.safe_to_skip)),
|
|
171
|
+
("Open clarifications", str(ctx.open_clarifications)),
|
|
172
|
+
("Approval", html.escape(ctx.approval)),
|
|
173
|
+
("Recommended option", html.escape(ctx.recommended_option or "--")),
|
|
174
|
+
("Recommended command", f"<code>{html.escape(ctx.recommended_command or '--')}</code>"),
|
|
175
|
+
)
|
|
176
|
+
summary = "".join(f"<dt>{label}</dt><dd>{value}</dd>" for label, value in rows)
|
|
177
|
+
return (
|
|
178
|
+
'<section class="reader-dashboard" aria-label="Reader summary" data-reader-section="action">\n'
|
|
179
|
+
' <div class="reader-dashboard-head">\n'
|
|
180
|
+
" <h2>Reader Summary</h2>\n"
|
|
181
|
+
' <div class="reader-mode-controls" aria-label="Reading mode">\n'
|
|
182
|
+
' <button type="button" data-action="set-reader-mode" data-reader-mode="action" aria-pressed="true">Action</button>\n'
|
|
183
|
+
' <button type="button" data-action="set-reader-mode" data-reader-mode="audit" aria-pressed="false">Audit</button>\n'
|
|
184
|
+
' <button type="button" data-action="set-reader-mode" data-reader-mode="full" aria-pressed="false">Full</button>\n'
|
|
185
|
+
" </div>\n"
|
|
186
|
+
" </div>\n"
|
|
187
|
+
f' <dl class="reader-summary-grid">{summary}</dl>\n'
|
|
188
|
+
"</section>\n"
|
|
144
189
|
)
|
|
145
190
|
|
|
146
191
|
|
|
@@ -171,7 +216,7 @@ def render_report_view_model(
|
|
|
171
216
|
f" <div>{title}</div>\n"
|
|
172
217
|
f" <button type=\"button\" data-action=\"export-user-response\">Export user response</button>\n"
|
|
173
218
|
f"</header>\n"
|
|
174
|
-
f"<main>{model.body_html}</main>\n"
|
|
219
|
+
f"<main>{_reader_dashboard(model.reader_ctx)}{model.body_html}</main>\n"
|
|
175
220
|
f"{_plan_approval_section(model.approval_ctx, run_meta) if model.approval_ctx else ''}"
|
|
176
221
|
f"<footer class=\"report-footer\">\n"
|
|
177
222
|
f" <button type=\"button\" data-action=\"export-user-response\">Export user response</button>\n"
|
|
@@ -202,6 +247,7 @@ def render_html(
|
|
|
202
247
|
css: str,
|
|
203
248
|
js: str,
|
|
204
249
|
approval_ctx: PlanApprovalContext | None = None,
|
|
250
|
+
reader_ctx: ReaderDashboardContext | None = None,
|
|
205
251
|
) -> str:
|
|
206
252
|
"""Return a single self-contained HTML document for ``src_md``.
|
|
207
253
|
|
|
@@ -213,6 +259,7 @@ def render_html(
|
|
|
213
259
|
src_md,
|
|
214
260
|
run_meta=run_meta,
|
|
215
261
|
approval_ctx=approval_ctx,
|
|
262
|
+
reader_ctx=reader_ctx,
|
|
216
263
|
)
|
|
217
264
|
return render_report_view_model(model, css=css, js=js)
|
|
218
265
|
|
|
@@ -948,6 +995,17 @@ class PlanApprovalContext:
|
|
|
948
995
|
blocker_ids: tuple[str, ...]
|
|
949
996
|
|
|
950
997
|
|
|
998
|
+
def _load_report_data(src_md_path: Path) -> dict | None:
|
|
999
|
+
data_path = final_report_data_path(src_md_path)
|
|
1000
|
+
if not data_path.is_file():
|
|
1001
|
+
return None
|
|
1002
|
+
try:
|
|
1003
|
+
data = json.loads(data_path.read_text(encoding="utf-8"))
|
|
1004
|
+
except (OSError, json.JSONDecodeError):
|
|
1005
|
+
return None
|
|
1006
|
+
return data if isinstance(data, dict) else None
|
|
1007
|
+
|
|
1008
|
+
|
|
951
1009
|
def plan_approval_context(
|
|
952
1010
|
src_md_path: Path, src_text: str
|
|
953
1011
|
) -> PlanApprovalContext | None:
|
|
@@ -957,12 +1015,8 @@ def plan_approval_context(
|
|
|
957
1015
|
validator 가 같은 판정을 공유한다. recommendedOption 이 비거나 후보에 없으면
|
|
958
1016
|
첫 후보로 폴백한다 — 항상 정확히 한 옵션이 selected 가 되어 브라우저 자동선택분의
|
|
959
1017
|
묵시 Export 를 차단한다."""
|
|
960
|
-
|
|
961
|
-
if
|
|
962
|
-
return None
|
|
963
|
-
try:
|
|
964
|
-
data = json.loads(data_path.read_text(encoding="utf-8"))
|
|
965
|
-
except (OSError, json.JSONDecodeError):
|
|
1018
|
+
data = _load_report_data(src_md_path)
|
|
1019
|
+
if data is None:
|
|
966
1020
|
return None
|
|
967
1021
|
planning = data.get("implementationPlanning")
|
|
968
1022
|
if not isinstance(planning, dict):
|
|
@@ -994,6 +1048,83 @@ def plan_approval_context(
|
|
|
994
1048
|
)
|
|
995
1049
|
|
|
996
1050
|
|
|
1051
|
+
def reader_dashboard_context(
|
|
1052
|
+
src_md_path: Path,
|
|
1053
|
+
src_text: str,
|
|
1054
|
+
approval_ctx: PlanApprovalContext | None,
|
|
1055
|
+
) -> ReaderDashboardContext | None:
|
|
1056
|
+
data = _load_report_data(src_md_path)
|
|
1057
|
+
if data is None:
|
|
1058
|
+
return None
|
|
1059
|
+
reader = data.get("readerSummary")
|
|
1060
|
+
if not isinstance(reader, dict):
|
|
1061
|
+
reader = {}
|
|
1062
|
+
verdict = data.get("verdictCard")
|
|
1063
|
+
if not isinstance(verdict, dict):
|
|
1064
|
+
verdict = {}
|
|
1065
|
+
clarifications = parse_clarification_items(src_text) or []
|
|
1066
|
+
open_count = sum(
|
|
1067
|
+
1 for item in clarifications
|
|
1068
|
+
if re.fullmatch(r"C-\d+", item.row_id) and item.status in UNRESOLVED_STATUSES
|
|
1069
|
+
)
|
|
1070
|
+
recommended_option = ""
|
|
1071
|
+
planning = data.get("implementationPlanning")
|
|
1072
|
+
if isinstance(planning, dict):
|
|
1073
|
+
rec = planning.get("recommendedOption")
|
|
1074
|
+
recommended_option = rec.get("name") or "" if isinstance(rec, dict) else ""
|
|
1075
|
+
if approval_ctx is not None:
|
|
1076
|
+
recommended_option = approval_ctx.recommended_option
|
|
1077
|
+
return ReaderDashboardContext(
|
|
1078
|
+
decision=_reader_text(reader.get("decision")) or _reader_text(verdict.get("finalConclusion")),
|
|
1079
|
+
human_action_required=(
|
|
1080
|
+
_reader_text(reader.get("humanActionRequired"))
|
|
1081
|
+
or _reader_text(verdict.get("nextStep"))
|
|
1082
|
+
or "Review the report sections below."
|
|
1083
|
+
),
|
|
1084
|
+
blocking_items=(
|
|
1085
|
+
_reader_text(reader.get("blockingItems"))
|
|
1086
|
+
or _fallback_blocking_items(open_count, approval_ctx)
|
|
1087
|
+
),
|
|
1088
|
+
safe_to_skip=(
|
|
1089
|
+
_reader_text(reader.get("safeToSkip"))
|
|
1090
|
+
or "Audit sections can wait until a deeper review."
|
|
1091
|
+
),
|
|
1092
|
+
recommended_command=(
|
|
1093
|
+
_reader_text(reader.get("recommendedCommand"))
|
|
1094
|
+
or _reader_text(verdict.get("nextStep"))
|
|
1095
|
+
),
|
|
1096
|
+
open_clarifications=open_count,
|
|
1097
|
+
approval=_approval_label(data, approval_ctx),
|
|
1098
|
+
recommended_option=recommended_option,
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
def _reader_text(value: object) -> str:
|
|
1103
|
+
return value.strip() if isinstance(value, str) else ""
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
def _fallback_blocking_items(
|
|
1107
|
+
open_count: int, approval_ctx: PlanApprovalContext | None
|
|
1108
|
+
) -> str:
|
|
1109
|
+
if approval_ctx is not None and approval_ctx.blocker_ids:
|
|
1110
|
+
return ", ".join(approval_ctx.blocker_ids)
|
|
1111
|
+
if open_count:
|
|
1112
|
+
return f"{open_count} open clarification item(s)."
|
|
1113
|
+
return "No open clarification items."
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
def _approval_label(data: dict, approval_ctx: PlanApprovalContext | None) -> str:
|
|
1117
|
+
frontmatter = data.get("frontmatter")
|
|
1118
|
+
approved = frontmatter.get("approved") if isinstance(frontmatter, dict) else False
|
|
1119
|
+
if approved is True:
|
|
1120
|
+
return "approved"
|
|
1121
|
+
if approval_ctx is not None:
|
|
1122
|
+
return "blocked" if approval_ctx.disabled_reason else "ready"
|
|
1123
|
+
verdict = data.get("verdictCard")
|
|
1124
|
+
required = verdict.get("approvalRequired") if isinstance(verdict, dict) else False
|
|
1125
|
+
return "required" if required else "not required"
|
|
1126
|
+
|
|
1127
|
+
|
|
997
1128
|
def _resolve_recommended_option(rec_name: str, names: tuple[str, ...]) -> str:
|
|
998
1129
|
"""``recommendedOption.name`` 을 후보 이름에 맞춘다. 정확 일치가 없으면
|
|
999
1130
|
접두 일치(후보가 ``(RECOMMENDED)`` 같은 표식 접미를 더 달고 있는 흔한 경우)를
|
|
@@ -1095,15 +1226,23 @@ def render_html_view(
|
|
|
1095
1226
|
"the HTML view."
|
|
1096
1227
|
)
|
|
1097
1228
|
approval_ctx = plan_approval_context(src_md_path, src_text)
|
|
1098
|
-
|
|
1229
|
+
reader_ctx = reader_dashboard_context(src_md_path, src_text, approval_ctx)
|
|
1230
|
+
has_clarifications = report_has_clarification_items(src_text)
|
|
1231
|
+
if not has_clarifications and approval_ctx is None and reader_ctx is None:
|
|
1099
1232
|
if html_path.is_file():
|
|
1100
1233
|
html_path.unlink()
|
|
1101
1234
|
return None
|
|
1102
1235
|
html_text = render_html(
|
|
1103
|
-
src_text,
|
|
1236
|
+
src_text,
|
|
1237
|
+
run_meta=run_meta,
|
|
1238
|
+
css=css,
|
|
1239
|
+
js=js,
|
|
1240
|
+
approval_ctx=approval_ctx,
|
|
1241
|
+
reader_ctx=reader_ctx,
|
|
1104
1242
|
)
|
|
1105
1243
|
html_path.write_text(html_text, encoding="utf-8")
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1244
|
+
if has_clarifications or approval_ctx is not None:
|
|
1245
|
+
# 사용자 확인(폼/승인)이 필요한 보고서 — Export 파일의 저장 위치를 미리
|
|
1246
|
+
# 만들어 사용자가 디렉토리를 만들 필요가 없게 한다 (reports/ 의 sibling).
|
|
1247
|
+
user_responses_dir_for_report(src_md_path).mkdir(parents=True, exist_ok=True)
|
|
1109
1248
|
return html_path
|
|
@@ -1412,7 +1412,7 @@ def _resolve_model_bindings(inp: PrepareInputs, workers: list[str]) -> _ModelBin
|
|
|
1412
1412
|
)
|
|
1413
1413
|
|
|
1414
1414
|
|
|
1415
|
-
|
|
1415
|
+
StageRunClaim = _implementation_stage.StageRunClaim
|
|
1416
1416
|
|
|
1417
1417
|
|
|
1418
1418
|
def _implementation_stage_prepare_error(
|
|
@@ -1421,17 +1421,17 @@ def _implementation_stage_prepare_error(
|
|
|
1421
1421
|
return PrepareError(str(exc))
|
|
1422
1422
|
|
|
1423
1423
|
|
|
1424
|
-
def
|
|
1424
|
+
def _claim_implementation_stage_run(
|
|
1425
1425
|
inp: PrepareInputs,
|
|
1426
1426
|
ctx_stage_map: list,
|
|
1427
1427
|
task_group_segment: str,
|
|
1428
1428
|
task_id_segment: str,
|
|
1429
1429
|
task_key: str,
|
|
1430
1430
|
executor_worktree_status: str,
|
|
1431
|
-
) ->
|
|
1432
|
-
"""Compatibility wrapper for implementation stage
|
|
1431
|
+
) -> StageRunClaim:
|
|
1432
|
+
"""Compatibility wrapper for claiming an implementation stage run."""
|
|
1433
1433
|
try:
|
|
1434
|
-
return _implementation_stage.
|
|
1434
|
+
return _implementation_stage.claim_implementation_stage_run(
|
|
1435
1435
|
inp,
|
|
1436
1436
|
ctx_stage_map,
|
|
1437
1437
|
task_group_segment,
|
|
@@ -1443,14 +1443,14 @@ def _select_and_provision_implementation_stage(
|
|
|
1443
1443
|
raise _implementation_stage_prepare_error(exc) from exc
|
|
1444
1444
|
|
|
1445
1445
|
|
|
1446
|
-
def
|
|
1446
|
+
def _publish_stage_run_claim(
|
|
1447
1447
|
inp: PrepareInputs,
|
|
1448
1448
|
ctx: dict,
|
|
1449
1449
|
ctx_stage_map: list,
|
|
1450
|
-
|
|
1450
|
+
claim: StageRunClaim,
|
|
1451
1451
|
) -> None:
|
|
1452
|
-
"""Compatibility wrapper for publishing
|
|
1453
|
-
_implementation_stage.
|
|
1452
|
+
"""Compatibility wrapper for publishing a Stage Run Claim."""
|
|
1453
|
+
_implementation_stage.publish_stage_run_claim(inp, ctx, ctx_stage_map, claim)
|
|
1454
1454
|
|
|
1455
1455
|
|
|
1456
1456
|
def _git_out(cwd, *args) -> str:
|
|
@@ -1898,7 +1898,7 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
1898
1898
|
# Identity segments derived with the SAME slugify rule compute_run_paths
|
|
1899
1899
|
# uses, so they match ctx["TASK_GROUP_SEGMENT"]/["TASK_ID_SEGMENT"]
|
|
1900
1900
|
# byte-for-byte. We need them BEFORE run-path compute because
|
|
1901
|
-
# implementation
|
|
1901
|
+
# implementation Stage Run Claim depends on the task-worktree degrade
|
|
1902
1902
|
# status, and the run path itself is stage-namespaced.
|
|
1903
1903
|
task_group_segment = slugify(inp.task_group)
|
|
1904
1904
|
task_id_segment = slugify(inp.task_id)
|
|
@@ -1908,7 +1908,7 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
1908
1908
|
# One worktree per task-key: requirements-discovery, error-analysis,
|
|
1909
1909
|
# implementation-planning and implementation phases of the same task
|
|
1910
1910
|
# all share this directory and branch. Runs BEFORE run-path compute: its
|
|
1911
|
-
# degrade status (skipped-*) feeds implementation
|
|
1911
|
+
# degrade status (skipped-*) feeds implementation Stage Run Claim, and
|
|
1912
1912
|
# the resolved stage namespaces the run path.
|
|
1913
1913
|
#
|
|
1914
1914
|
# The whole check→select→`git worktree add`→reserve sequence (task
|
|
@@ -1944,26 +1944,26 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
1944
1944
|
f"task worktree provisioning failed: {exc}"
|
|
1945
1945
|
) from exc
|
|
1946
1946
|
|
|
1947
|
-
# ---- implementation
|
|
1947
|
+
# ---- implementation Stage Run Claim (path-independent) ----
|
|
1948
1948
|
# Resolve + provision the stage BEFORE run-path compute so RUN_DIR
|
|
1949
1949
|
# lands in runs/implementation/stage-<N>. The registry stage-key is
|
|
1950
1950
|
# reserved exactly once here (inside provision_stage_worktree), and
|
|
1951
|
-
# the surrounding mutex makes the registry read in
|
|
1952
|
-
# and that reserve atomic. Other task-types skip this
|
|
1951
|
+
# the surrounding mutex makes the registry read in the claim
|
|
1952
|
+
# and that reserve atomic. Other task-types skip this claim;
|
|
1953
1953
|
# single-stage final-verification threads its explicit stage via
|
|
1954
1954
|
# fv_stage_arg, everything else keeps stage_arg=None (flat paths).
|
|
1955
1955
|
if inp.task_type == "implementation":
|
|
1956
|
-
|
|
1956
|
+
stage_run_claim = _claim_implementation_stage_run(
|
|
1957
1957
|
inp, ctx_stage_map, task_group_segment, task_id_segment,
|
|
1958
1958
|
task_key, worktree.status,
|
|
1959
1959
|
)
|
|
1960
|
-
stage_arg =
|
|
1960
|
+
stage_arg = stage_run_claim.stage
|
|
1961
1961
|
# Drop any stale waiver on this stage so the run actually verifies
|
|
1962
1962
|
# conformance (kept inside the per-task-key mutex so concurrent
|
|
1963
1963
|
# same-task runs don't race the manifest write).
|
|
1964
|
-
_clear_stale_stage_waiver(inp, project_root,
|
|
1964
|
+
_clear_stale_stage_waiver(inp, project_root, stage_run_claim.stage)
|
|
1965
1965
|
else:
|
|
1966
|
-
|
|
1966
|
+
stage_run_claim = None
|
|
1967
1967
|
stage_arg = fv_stage_arg
|
|
1968
1968
|
|
|
1969
1969
|
ctx = compute_and_write_run_context(
|
|
@@ -1988,13 +1988,13 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
1988
1988
|
),
|
|
1989
1989
|
})
|
|
1990
1990
|
|
|
1991
|
-
# implementation: override the task-worktree fields with the
|
|
1992
|
-
# STAGE worktree
|
|
1991
|
+
# implementation: override the task-worktree fields with the claimed
|
|
1992
|
+
# STAGE worktree. Must run
|
|
1993
1993
|
# AFTER the task-worktree fields above (mirrors the original ordering
|
|
1994
1994
|
# where stage reservation overrode them post task-provision).
|
|
1995
1995
|
if inp.task_type == "implementation":
|
|
1996
|
-
|
|
1997
|
-
inp, ctx, ctx_stage_map,
|
|
1996
|
+
_publish_stage_run_claim(
|
|
1997
|
+
inp, ctx, ctx_stage_map, stage_run_claim,
|
|
1998
1998
|
)
|
|
1999
1999
|
|
|
2000
2000
|
if inp.render_only:
|
|
@@ -2166,10 +2166,10 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
2166
2166
|
_provision_settings_symlink(inp)
|
|
2167
2167
|
|
|
2168
2168
|
concurrent_run: dict = {}
|
|
2169
|
-
if
|
|
2169
|
+
if stage_run_claim is not None and stage_run_claim.concurrent_stages:
|
|
2170
2170
|
concurrent_run = {
|
|
2171
2171
|
"detected": True,
|
|
2172
|
-
"active_stages":
|
|
2172
|
+
"active_stages": stage_run_claim.concurrent_stages,
|
|
2173
2173
|
}
|
|
2174
2174
|
return PrepareOutputs(
|
|
2175
2175
|
ctx=ctx,
|
|
@@ -306,6 +306,32 @@ def order_stage_closure(
|
|
|
306
306
|
return out
|
|
307
307
|
|
|
308
308
|
|
|
309
|
+
def downstream_stage_closure(
|
|
310
|
+
stages: list[tuple[int, list[int]]],
|
|
311
|
+
seed: set[int],
|
|
312
|
+
) -> set[int]:
|
|
313
|
+
"""Stages that (transitively) depend on any stage in ``seed``, plus ``seed``.
|
|
314
|
+
|
|
315
|
+
``stages`` mirrors ``order_stage_closure``: ``(stage_number, [depends_on_numbers])``.
|
|
316
|
+
``order_stage_closure`` walks dependencies (upstream); this walks dependents
|
|
317
|
+
(downstream) — the reverse edge — so an answered clarification that changes a
|
|
318
|
+
seed stage re-verifies everything built on top of it.
|
|
319
|
+
"""
|
|
320
|
+
dependents: dict[int, list[int]] = {}
|
|
321
|
+
for num, deps in stages:
|
|
322
|
+
for dep in deps:
|
|
323
|
+
dependents.setdefault(dep, []).append(num)
|
|
324
|
+
result = set(seed)
|
|
325
|
+
queue = list(seed)
|
|
326
|
+
while queue:
|
|
327
|
+
cur = queue.pop()
|
|
328
|
+
for child in dependents.get(cur, []):
|
|
329
|
+
if child not in result:
|
|
330
|
+
result.add(child)
|
|
331
|
+
queue.append(child)
|
|
332
|
+
return result
|
|
333
|
+
|
|
334
|
+
|
|
309
335
|
def commit_is_ancestor(project_root: Path, ancestor: str, descendant: str) -> bool:
|
|
310
336
|
"""True iff ``ancestor`` is an ancestor of ``descendant`` in git history."""
|
|
311
337
|
from .worktree import is_ancestor
|
|
@@ -881,7 +881,7 @@ def provision_stage_worktree(
|
|
|
881
881
|
before re-raising so a retry is not blocked.
|
|
882
882
|
|
|
883
883
|
Concurrency: callers must hold ``locks.worktree_provision_mutex`` for
|
|
884
|
-
the task-key, acquired BEFORE
|
|
884
|
+
the task-key, acquired BEFORE the Stage Run Claim reads the registry
|
|
885
885
|
(run.py does) — otherwise two `--stage auto` runs can select the same
|
|
886
886
|
stage and the loser silently enters the winner's worktree via the
|
|
887
887
|
"reused" path. flock is non-reentrant, hence the lock lives at the
|
|
@@ -83,6 +83,28 @@ def _session_meta_from_record(rec: dict) -> tuple[str | None, str | None]:
|
|
|
83
83
|
return agent, model
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
_SUBAGENT_FILENAME_RE = re.compile(r"^agent-a(?P<agent>.+)-[0-9a-f]{8,}$")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _agent_name_from_subagent_path(jsonl_path: Path) -> str | None:
|
|
90
|
+
if jsonl_path.parent.name != "subagents":
|
|
91
|
+
return None
|
|
92
|
+
match = _SUBAGENT_FILENAME_RE.match(jsonl_path.stem)
|
|
93
|
+
return match.group("agent") if match else None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _iter_claude_session_jsonls(
|
|
97
|
+
proj_dir: Path,
|
|
98
|
+
subagent_parent_sids: list[str] | None = None,
|
|
99
|
+
):
|
|
100
|
+
yield from sorted(proj_dir.glob("*.jsonl"))
|
|
101
|
+
if subagent_parent_sids is None:
|
|
102
|
+
yield from sorted(proj_dir.glob("*/subagents/*.jsonl"))
|
|
103
|
+
return
|
|
104
|
+
for sid in sorted({sid for sid in subagent_parent_sids if sid}):
|
|
105
|
+
yield from sorted((proj_dir / sid / "subagents").glob("*.jsonl"))
|
|
106
|
+
|
|
107
|
+
|
|
86
108
|
def _advance_usage_scan(jsonl_path: Path, usage_state: dict) -> dict:
|
|
87
109
|
"""`usage_state['offset']` 이후의 완결 라인을 읽어 이벤트를 커밋하고,
|
|
88
110
|
개행 없는 마지막 라인은 transient 로만 반영한 view 를 돌려준다.
|
|
@@ -230,7 +252,8 @@ def claude_session_totals(
|
|
|
230
252
|
save_cache(jsonl_path, cache)
|
|
231
253
|
else:
|
|
232
254
|
view = _advance_usage_scan(jsonl_path, fresh_cache()["usage"])
|
|
233
|
-
|
|
255
|
+
agent_name = view["agentName"] or _agent_name_from_subagent_path(jsonl_path)
|
|
256
|
+
return _totals_from_events(view["events"], agent_name,
|
|
234
257
|
view["model"], since, until)
|
|
235
258
|
|
|
236
259
|
|
|
@@ -312,7 +335,7 @@ def find_claude_team_sessions(
|
|
|
312
335
|
if not proj_dir.is_dir():
|
|
313
336
|
return out
|
|
314
337
|
needles_lower = [f'"teamname":"{n.lower()}"' for n in team_needles if n]
|
|
315
|
-
for p in proj_dir
|
|
338
|
+
for p in _iter_claude_session_jsonls(proj_dir):
|
|
316
339
|
for needle_lower in needles_lower:
|
|
317
340
|
if incremental:
|
|
318
341
|
cache = load_cache(p)
|
|
@@ -333,26 +356,33 @@ def find_claude_team_sessions(
|
|
|
333
356
|
_TEAM_TAG_NEEDLE = '"teamname":"'
|
|
334
357
|
|
|
335
358
|
|
|
359
|
+
def _agent_name_matches(agent_name: str | None, prefixes: list[str]) -> bool:
|
|
360
|
+
if not agent_name:
|
|
361
|
+
return False
|
|
362
|
+
for prefix in prefixes:
|
|
363
|
+
if agent_name == prefix or agent_name.startswith(f"{prefix}-"):
|
|
364
|
+
return True
|
|
365
|
+
return False
|
|
366
|
+
|
|
367
|
+
|
|
336
368
|
def find_claude_agent_sessions(
|
|
337
369
|
cwd: Path,
|
|
338
370
|
agent_prefixes: list[str],
|
|
339
371
|
projects_root: Path | None = None,
|
|
340
372
|
*,
|
|
341
373
|
incremental: bool = False,
|
|
374
|
+
subagent_parent_sids: list[str] | None = None,
|
|
342
375
|
) -> dict[str, Path]:
|
|
343
|
-
"""Map
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
한다. 윈도우가 겹치는 두 no-team run 이 같은 role 을 dispatch 한 경우의
|
|
354
|
-
교차 귀속은 구조적으로 분리 불가 — usage 블록의 sessionId 목록으로만
|
|
355
|
-
추적 가능하다.
|
|
376
|
+
"""Map worker-like Claude jsonls by agent name or nested subagent filename.
|
|
377
|
+
|
|
378
|
+
Legacy top-level worker jsonls record `agentName` in the JSONL body. Newer
|
|
379
|
+
in-process subagents can instead live under
|
|
380
|
+
`<lead-session>/subagents/agent-a<name>-<hash>.jsonl` with no body
|
|
381
|
+
`agentName`, so this finder also matches that filename form.
|
|
382
|
+
|
|
383
|
+
Callers can pass `subagent_parent_sids` to limit nested subagents to the
|
|
384
|
+
current lead session directory. Top-level fallback discovery remains broad
|
|
385
|
+
and must still be filtered by the run window after totals are read.
|
|
356
386
|
"""
|
|
357
387
|
proj_dir = claude_project_dir(cwd, projects_root)
|
|
358
388
|
out: dict[str, Path] = {}
|
|
@@ -361,20 +391,29 @@ def find_claude_agent_sessions(
|
|
|
361
391
|
agent_needles = [f'"agentname":"{p.lower()}' for p in agent_prefixes if p]
|
|
362
392
|
if not agent_needles:
|
|
363
393
|
return out
|
|
364
|
-
for p in proj_dir
|
|
394
|
+
for p in _iter_claude_session_jsonls(proj_dir, subagent_parent_sids):
|
|
395
|
+
path_agent = _agent_name_from_subagent_path(p)
|
|
396
|
+
filename_matched = _agent_name_matches(path_agent, agent_prefixes)
|
|
397
|
+
scoped_subagent = (
|
|
398
|
+
filename_matched
|
|
399
|
+
and path_agent is not None
|
|
400
|
+
and subagent_parent_sids is not None
|
|
401
|
+
)
|
|
365
402
|
if incremental:
|
|
366
403
|
cache = load_cache(p)
|
|
367
|
-
matched = any(
|
|
404
|
+
matched = filename_matched or any(
|
|
405
|
+
_cached_needle_scan(p, cache, n) for n in agent_needles
|
|
406
|
+
)
|
|
368
407
|
team_tagged = matched and _cached_needle_scan(p, cache, _TEAM_TAG_NEEDLE)
|
|
369
408
|
save_cache(p, cache)
|
|
370
409
|
else:
|
|
371
|
-
matched = any(
|
|
410
|
+
matched = filename_matched or any(
|
|
372
411
|
_needle_scan(p, {"offset": 0, "found": False}, n)
|
|
373
412
|
for n in agent_needles
|
|
374
413
|
)
|
|
375
414
|
team_tagged = matched and _needle_scan(
|
|
376
415
|
p, {"offset": 0, "found": False}, _TEAM_TAG_NEEDLE
|
|
377
416
|
)
|
|
378
|
-
if matched and not team_tagged:
|
|
417
|
+
if matched and (scoped_subagent or not team_tagged):
|
|
379
418
|
out[p.stem] = p
|
|
380
419
|
return out
|
|
@@ -653,8 +653,10 @@ def collect(team_state_path: Path, project_root: Path | None = None, *,
|
|
|
653
653
|
for w in state.get("workers", [])
|
|
654
654
|
for prefix in match_prefixes(w.get("workerId") or "")
|
|
655
655
|
]
|
|
656
|
+
subagent_parent_sids = [lead_sid] if lead_sid else []
|
|
656
657
|
agent_sessions = find_claude_agent_sessions(
|
|
657
|
-
cwd, worker_prefix_pool, incremental=incremental
|
|
658
|
+
cwd, worker_prefix_pool, incremental=incremental,
|
|
659
|
+
subagent_parent_sids=subagent_parent_sids,
|
|
658
660
|
)
|
|
659
661
|
for sid, path in agent_sessions.items():
|
|
660
662
|
if sid == lead_sid or sid in claude_sessions:
|
|
@@ -12,6 +12,9 @@ Currently vendored:
|
|
|
12
12
|
(`_speedups.c` / `_speedups.pyi`) is stripped; the pure-python
|
|
13
13
|
`_native` fallback is used at import time.
|
|
14
14
|
- networkx (BSD-3-Clause) — graph model + community detection for okstra-graphify.
|
|
15
|
+
Per-package `tests/` suites and the top-level `conftest.py` are stripped;
|
|
16
|
+
okstra never runs networkx's own test suite. On re-vendor, drop every
|
|
17
|
+
`**/tests/` directory and `conftest.py` again.
|
|
15
18
|
- graphify 0.4.23 (MIT) — knowledge-graph pipeline for okstra-graphify. tree-sitter
|
|
16
19
|
native bindings are NOT vendored; okstra-graphify only indexes .okstra/ docs.
|
|
17
20
|
|
|
@@ -154,6 +154,26 @@
|
|
|
154
154
|
}
|
|
155
155
|
},
|
|
156
156
|
|
|
157
|
+
"readerSummary": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"description": "Optional reader-first summary rendered near the top of the Markdown report and reused by the HTML reader dashboard. Existing v1.0 data.json files may omit it; new report-writer output should populate it to keep the first-read path short.",
|
|
160
|
+
"required": [
|
|
161
|
+
"decision",
|
|
162
|
+
"humanActionRequired",
|
|
163
|
+
"blockingItems",
|
|
164
|
+
"safeToSkip",
|
|
165
|
+
"recommendedCommand"
|
|
166
|
+
],
|
|
167
|
+
"additionalProperties": false,
|
|
168
|
+
"properties": {
|
|
169
|
+
"decision": { "type": "string", "minLength": 1 },
|
|
170
|
+
"humanActionRequired": { "type": "string", "minLength": 1 },
|
|
171
|
+
"blockingItems": { "type": "string", "minLength": 1 },
|
|
172
|
+
"safeToSkip": { "type": "string", "minLength": 1 },
|
|
173
|
+
"recommendedCommand": { "type": "string", "minLength": 1 }
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
157
177
|
"rationale": {
|
|
158
178
|
"type": "object",
|
|
159
179
|
"description": "## 작업 배경과 근거 — reviewer-facing narrative answering the four questions in order. Each field is prose (not a table) and MUST carry at least one evidence reference (path:line, a report ID such as C-001/F-013/§5.4, or another cited source) OR an explicit insufficiency marker (e.g. '근거 불충분'). validate-run.py fails a field with neither. Required for every task-type.",
|
|
@@ -425,6 +445,24 @@
|
|
|
425
445
|
"skippedAdrCandidates": {
|
|
426
446
|
"type": "array",
|
|
427
447
|
"items": { "$ref": "#/$defs/SkippedAdrCandidate" }
|
|
448
|
+
},
|
|
449
|
+
"incrementalDecision": {
|
|
450
|
+
"type": "object",
|
|
451
|
+
"description": "Verbatim `okstra incremental-scope` output for a clarification re-run. Present only when this implementation-planning run scoped itself against a prior run; drives the Section 0 incremental audit block.",
|
|
452
|
+
"required": ["mode", "reverifyStages", "carryStages", "reason"],
|
|
453
|
+
"additionalProperties": false,
|
|
454
|
+
"properties": {
|
|
455
|
+
"mode": { "type": "string", "enum": ["incremental", "full"] },
|
|
456
|
+
"reverifyStages": {
|
|
457
|
+
"type": "array",
|
|
458
|
+
"items": { "type": "integer" }
|
|
459
|
+
},
|
|
460
|
+
"carryStages": {
|
|
461
|
+
"type": "array",
|
|
462
|
+
"items": { "type": "integer" }
|
|
463
|
+
},
|
|
464
|
+
"reason": { "type": "string", "minLength": 1 }
|
|
465
|
+
}
|
|
428
466
|
}
|
|
429
467
|
}
|
|
430
468
|
},
|
|
@@ -1480,6 +1518,7 @@
|
|
|
1480
1518
|
"id": { "type": "string", "minLength": 1 },
|
|
1481
1519
|
"subject": { "type": "string", "minLength": 1 },
|
|
1482
1520
|
"sourceSection": { "type": "string" },
|
|
1521
|
+
"carriedForwardFromSeq": { "type": "string" },
|
|
1483
1522
|
"clarificationId": {
|
|
1484
1523
|
"anyOf": [
|
|
1485
1524
|
{ "type": "null" },
|