okstra 0.113.0 → 0.115.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/README.kr.md +2 -2
- package/README.md +2 -2
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/{okstra-brief.md → okstra-brief-gen.md} +3 -3
- package/docs/for-ai/skills/okstra-inspect.md +1 -1
- package/docs/kr/architecture.md +11 -11
- package/docs/kr/cli.md +1 -1
- package/docs/kr/follow-ups/2026-07-10-final-report-option-3.md +51 -0
- package/docs/project-structure-overview.md +2 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +1 -0
- package/runtime/prompts/lead/context-loader.md +2 -2
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/implementation-planning.md +1 -1
- package/runtime/prompts/profiles/requirements-discovery.md +2 -2
- package/runtime/python/okstra_ctl/fix_cycles.py +1 -1
- package/runtime/python/okstra_ctl/implementation_stage.py +46 -39
- package/runtime/python/okstra_ctl/report_views.py +151 -12
- package/runtime/python/okstra_ctl/run.py +24 -24
- package/runtime/python/okstra_ctl/wizard.py +3 -3
- 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 +20 -0
- package/runtime/skills/{okstra-brief → okstra-brief-gen}/SKILL.md +6 -6
- package/runtime/skills/{okstra-brief → okstra-brief-gen}/references/reporter-confirmations.md +1 -1
- package/runtime/skills/{okstra-brief → okstra-brief-gen}/references/tracker-recursion.md +1 -1
- package/runtime/skills/okstra-inspect/SKILL.md +2 -2
- package/runtime/templates/reports/brief.template.md +2 -2
- package/runtime/templates/reports/final-report.template.md +14 -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-brief.py +5 -5
- package/runtime/validators/validate-brief.sh +1 -1
- package/src/commands/lifecycle/uninstall.mjs +2 -1
- package/src/lib/skill-catalog.mjs +2 -1
- 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,
|
|
@@ -209,7 +209,7 @@ def _brief_suggestions(path: Path) -> tuple[str, str]:
|
|
|
209
209
|
|
|
210
210
|
- ``task_group`` ← frontmatter ``task-group``.
|
|
211
211
|
- ``task_id`` ← frontmatter ``brief-id`` (which matches the
|
|
212
|
-
filename stem in okstra-brief output and is the
|
|
212
|
+
filename stem in okstra-brief-gen output and is the
|
|
213
213
|
strongest single identifier of the task).
|
|
214
214
|
|
|
215
215
|
A brief without frontmatter, or with placeholder values, yields two
|
|
@@ -1261,9 +1261,9 @@ def _recently_used_brief_times(state: WizardState) -> dict[str, float]:
|
|
|
1261
1261
|
|
|
1262
1262
|
|
|
1263
1263
|
def _suggest_group_briefs(state: WizardState, limit: int = 6) -> list[str]:
|
|
1264
|
-
"""Return recent okstra-brief outputs for the selected task-group.
|
|
1264
|
+
"""Return recent okstra-brief-gen outputs for the selected task-group.
|
|
1265
1265
|
|
|
1266
|
-
``okstra-brief`` writes to ``.okstra/briefs/<task-group>/**/*.md``. The
|
|
1266
|
+
``okstra-brief-gen`` writes to ``.okstra/briefs/<task-group>/**/*.md``. The
|
|
1267
1267
|
wizard exposes those paths after task-group selection so users can pick a
|
|
1268
1268
|
generated brief instead of typing its path. Paths are project-relative.
|
|
1269
1269
|
"""
|
|
@@ -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.",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: okstra-brief
|
|
2
|
+
name: okstra-brief-gen
|
|
3
3
|
description: Use when the user wants to generate a task brief file for okstra from a requirements document, an existing markdown file, an issue-tracker ticket (Linear / Jira / GitHub / Notion), a link URL, conversation context, or short user input. Produces the markdown brief consumed by `okstra-run` Step 5 (task-brief). Trigger words include "okstra brief", "make a brief", "brief 생성", "요구사항 brief 만들어", "okstra 입력 만들어", "task brief 작성", "이 티켓으로 brief", "이 링크로 brief".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# okstra-brief
|
|
6
|
+
# okstra-brief-gen
|
|
7
7
|
|
|
8
8
|
Generate a single `task brief` markdown file under the okstra project domain
|
|
9
9
|
(`.okstra/briefs/<task-group>/<task-id>.md`) so it can be fed to
|
|
@@ -41,21 +41,21 @@ cross-verification and would conflict with anything decided here.
|
|
|
41
41
|
## Intended chain
|
|
42
42
|
|
|
43
43
|
```
|
|
44
|
-
okstra-brief (reporter-input variant)
|
|
44
|
+
okstra-brief-gen (reporter-input variant)
|
|
45
45
|
→ okstra-run (requirements-discovery | error-analysis)
|
|
46
46
|
→ okstra-run (implementation-planning)
|
|
47
47
|
→ okstra-run (implementation)
|
|
48
48
|
→ okstra-run (final-verification)
|
|
49
49
|
→ okstra-run (release-handoff)
|
|
50
50
|
|
|
51
|
-
okstra-brief (codebase-scan variant)
|
|
51
|
+
okstra-brief-gen (codebase-scan variant)
|
|
52
52
|
→ okstra-run (improvement-discovery)
|
|
53
53
|
→ okstra-run (implementation-planning)
|
|
54
54
|
→ okstra-run (implementation)
|
|
55
55
|
→ okstra-run (final-verification)
|
|
56
56
|
→ okstra-run (release-handoff)
|
|
57
57
|
|
|
58
|
-
okstra-brief (error-feedback variant)
|
|
58
|
+
okstra-brief-gen (error-feedback variant)
|
|
59
59
|
→ okstra-run (error-analysis)
|
|
60
60
|
→ okstra-run (implementation-planning)
|
|
61
61
|
→ okstra-run (implementation)
|
|
@@ -737,7 +737,7 @@ The installed template `~/.okstra/templates/reports/brief.template.md` is the by
|
|
|
737
737
|
notion | url | user-input`).
|
|
738
738
|
- `task-group` echoes the Step 2a value.
|
|
739
739
|
- `created` is the brief's creation date (`YYYY-MM-DD`).
|
|
740
|
-
- `generator: okstra-brief` is a fixed value.
|
|
740
|
+
- `generator: okstra-brief-gen` is a fixed value.
|
|
741
741
|
- `reporter-confirmations` is set by Step 6.5 (`complete | partial | pending |
|
|
742
742
|
skipped`).
|
|
743
743
|
- `scope` is `codebase` for the codebase-scan variant; omit it (⇒
|
package/runtime/skills/{okstra-brief → okstra-brief-gen}/references/reporter-confirmations.md
RENAMED
|
@@ -18,7 +18,7 @@ rows, sort by priority and ask only the top 12 this round:
|
|
|
18
18
|
|
|
19
19
|
Any rows beyond the cap are left unanswered in this run; set
|
|
20
20
|
`reporter-confirmations: partial` in the frontmatter and tell the user which
|
|
21
|
-
rows remain. The next `okstra-brief` re-run (or the downstream phase that
|
|
21
|
+
rows remain. The next `okstra-brief-gen` re-run (or the downstream phase that
|
|
22
22
|
consumes `Blocks=next-phase`) handles the remainder.
|
|
23
23
|
|
|
24
24
|
Each question carries:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Sub-ticket / child-issue recursion (tracker sources)
|
|
2
2
|
|
|
3
3
|
Read this only when a fetched tracker ticket shows one or more child references
|
|
4
|
-
(okstra-brief Step 1b sub-step 6). It defines how the child tree is discovered,
|
|
4
|
+
(okstra-brief-gen Step 1b sub-step 6). It defines how the child tree is discovered,
|
|
5
5
|
which briefs are emitted, and how re-runs stay idempotent.
|
|
6
6
|
|
|
7
7
|
## 1. Discover child references per tracker
|