okstra 0.207.0 → 0.208.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.md +3 -2
- package/dist/cli-registry.mjs +6 -0
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +1 -1
- package/dist/commands/lifecycle/doctor.mjs +1 -1
- package/dist/lib/skill-catalog.mjs +1 -0
- package/dist/lib/skill-catalog.mjs.map +1 -1
- package/docs/architecture/storage-model.md +14 -0
- package/docs/architecture.md +30 -9
- package/docs/cli.md +26 -22
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/project-structure-overview.md +15 -8
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/operations/explain-flow.json +6 -0
- package/runtime/bin/lib/okstra/cli.sh +1 -5
- package/runtime/bin/lib/okstra/globals.sh +0 -2
- package/runtime/bin/lib/okstra/usage.sh +5 -3
- package/runtime/bin/okstra.sh +0 -2
- package/runtime/prompts/duties/business-flow-investigator.json +14 -0
- package/runtime/prompts/lead/context-loader.md +1 -1
- package/runtime/prompts/lead/convergence.md +22 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +10 -6
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/lead/team-contract.md +12 -17
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/wizard/prompts.ko.json +0 -91
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +6 -0
- package/runtime/python/okstra_ctl/agent/invocation.py +1 -1
- package/runtime/python/okstra_ctl/agent/prompt_cli/batch.py +1 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +1 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +11 -125
- package/runtime/python/okstra_ctl/agent/standalone.py +183 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +39 -8
- package/runtime/python/okstra_ctl/assignment_resolver.py +7 -1
- package/runtime/python/okstra_ctl/brief_frontmatter.py +10 -0
- package/runtime/python/okstra_ctl/business_flow/__init__.py +4 -0
- package/runtime/python/okstra_ctl/business_flow/cli.py +134 -0
- package/runtime/python/okstra_ctl/business_flow/contracts.py +268 -0
- package/runtime/python/okstra_ctl/business_flow/engine.py +518 -0
- package/runtime/python/okstra_ctl/business_flow/hooks.py +221 -0
- package/runtime/python/okstra_ctl/business_flow/invocation.py +170 -0
- package/runtime/python/okstra_ctl/business_flow/report.py +49 -0
- package/runtime/python/okstra_ctl/business_flow/source.py +206 -0
- package/runtime/python/okstra_ctl/business_flow/store.py +388 -0
- package/runtime/python/okstra_ctl/convergence.py +173 -2
- package/runtime/python/okstra_ctl/convergence_critic_verify_prompt.py +18 -0
- package/runtime/python/okstra_ctl/convergence_provenance.py +8 -0
- package/runtime/python/okstra_ctl/coverage_census.py +596 -0
- package/runtime/python/okstra_ctl/design_surfaces.py +4 -0
- package/runtime/python/okstra_ctl/direct_work.py +1 -1
- package/runtime/python/okstra_ctl/dispatch_core.py +7 -3
- package/runtime/python/okstra_ctl/doctor.py +12 -6
- package/runtime/python/okstra_ctl/domain/role.py +1 -0
- package/runtime/python/okstra_ctl/group_context.py +5 -4
- package/runtime/python/okstra_ctl/legacy_model_selection.py +7 -51
- package/runtime/python/okstra_ctl/manager_split.py +4 -1
- package/runtime/python/okstra_ctl/manager_view.py +9 -3
- package/runtime/python/okstra_ctl/model_io/lines.py +1 -24
- package/runtime/python/okstra_ctl/model_io/renderers.py +54 -41
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/change_impact_analysis/profile.md +0 -8
- package/runtime/python/okstra_ctl/phases/error_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/error_analysis/profile.md +6 -8
- package/runtime/python/okstra_ctl/phases/feature_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/feature_analysis/profile.md +0 -8
- package/runtime/python/okstra_ctl/phases/final_verification/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/final_verification/profile.md +2 -8
- package/runtime/python/okstra_ctl/phases/implementation/boundary.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation/profile.md +0 -6
- package/runtime/python/okstra_ctl/phases/implementation/report_assets/implementation-input.template.md +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/authoring.py +4 -3
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/entry.py +1 -14
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/profile.md +5 -8
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/spec.md +3 -3
- package/runtime/python/okstra_ctl/phases/implementation_option_selection/validation.py +15 -5
- package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +9 -1
- package/runtime/python/okstra_ctl/phases/implementation_planning/boundary.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_planning/instructions/plan-body-verification.md +4 -2
- package/runtime/python/okstra_ctl/phases/implementation_planning/plan_body.py +25 -9
- package/runtime/python/okstra_ctl/phases/implementation_planning/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/implementation_planning/profile.md +7 -10
- package/runtime/python/okstra_ctl/phases/improvement_discovery/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/improvement_discovery/profile.md +4 -11
- package/runtime/python/okstra_ctl/phases/project_analysis/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/project_analysis/profile.md +0 -8
- package/runtime/python/okstra_ctl/phases/release_handoff/profile.md +1 -1
- package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +1 -1
- package/runtime/python/okstra_ctl/phases/requirements_discovery/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/requirements_discovery/profile.md +10 -8
- package/runtime/python/okstra_ctl/phases/requirements_discovery/spec.md +3 -3
- package/runtime/python/okstra_ctl/phases/technical_verification/profile.json +1 -1
- package/runtime/python/okstra_ctl/phases/technical_verification/profile.md +0 -4
- package/runtime/python/okstra_ctl/plan_items.py +1 -1
- package/runtime/python/okstra_ctl/render.py +10 -43
- package/runtime/python/okstra_ctl/render_final_report.py +3 -0
- package/runtime/python/okstra_ctl/report_assembly.py +15 -1
- package/runtime/python/okstra_ctl/report_finalize.py +40 -0
- package/runtime/python/okstra_ctl/report_html/render.py +3 -0
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +1 -2
- package/runtime/python/okstra_ctl/run.py +78 -409
- package/runtime/python/okstra_ctl/wizard/__init__.py +2 -24
- package/runtime/python/okstra_ctl/wizard/cli.py +3 -6
- package/runtime/python/okstra_ctl/wizard/confirmation.py +3 -35
- package/runtime/python/okstra_ctl/wizard/engine.py +2 -4
- package/runtime/python/okstra_ctl/wizard/ids.py +1 -88
- package/runtime/python/okstra_ctl/wizard/registry.py +36 -228
- package/runtime/python/okstra_ctl/wizard/render.py +2 -2
- package/runtime/python/okstra_ctl/wizard/roles.py +1 -3
- package/runtime/python/okstra_ctl/wizard/sources.py +9 -40
- package/runtime/python/okstra_ctl/wizard/state.py +36 -145
- package/runtime/python/okstra_ctl/wizard/statefile.py +27 -128
- package/runtime/python/okstra_ctl/wizard/steps_identity.py +22 -10
- package/runtime/python/okstra_ctl/wizard/steps_options.py +5 -4
- package/runtime/python/okstra_ctl/wizard/steps_roles.py +15 -565
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +9 -2
- package/runtime/schemas/business-flow-v1.schema.json +847 -0
- package/runtime/schemas/convergence-groups-v2.0.schema.json +7 -0
- package/runtime/skills/okstra-explain-flow/SKILL.md +42 -0
- package/runtime/skills/okstra-inspect/facets/history.md +5 -5
- package/runtime/skills/okstra-run/SKILL.md +2 -2
- package/runtime/templates/manager/view.template.html +7 -4
- package/runtime/templates/reports/business-flow.template.md +106 -0
- package/runtime/templates/reports/html/base.template.html +14 -1
- package/runtime/templates/reports/html/business-flow.template.html +31 -0
- package/runtime/templates/reports/html/i18n/en.json +1 -0
- package/runtime/templates/reports/html/i18n/ko.json +1 -0
- package/runtime/templates/worker-prompt-preamble.md +11 -2
- package/runtime/validators/checks/validate-prompt-metadata-01.py +10 -10
- package/runtime/validators/validate-run.py +70 -21
- package/runtime/validators/validate_analysis_report.py +21 -21
- package/runtime/python/okstra_ctl/workers.py +0 -133
|
@@ -55,6 +55,12 @@ from .phases.implementation_planning.entry import (
|
|
|
55
55
|
)
|
|
56
56
|
from . import fix_cycles
|
|
57
57
|
from .analysis_packet import build_analysis_packet
|
|
58
|
+
from .coverage_census import (
|
|
59
|
+
CensusError,
|
|
60
|
+
census_filename,
|
|
61
|
+
census_for_run,
|
|
62
|
+
render_census_section,
|
|
63
|
+
)
|
|
58
64
|
from . import group_context
|
|
59
65
|
from .stage_ledger import (
|
|
60
66
|
build_stage_ledger,
|
|
@@ -218,13 +224,6 @@ from .session import (
|
|
|
218
224
|
from .phases.release_handoff.entry import (
|
|
219
225
|
materialize_handoff_input, resolve_pr_template,
|
|
220
226
|
)
|
|
221
|
-
from .worker_artifacts import worker_provider_id
|
|
222
|
-
from .workers import (
|
|
223
|
-
normalize_workers,
|
|
224
|
-
resolve_optional_workers,
|
|
225
|
-
resolve_profile_workers,
|
|
226
|
-
validate_workers_against_profile,
|
|
227
|
-
)
|
|
228
227
|
from .workflow import compute_workflow_state, load_phase_boundary
|
|
229
228
|
from .locks import worktree_provision_mutex
|
|
230
229
|
from . import stage_targets as _stage_targets
|
|
@@ -528,7 +527,7 @@ def _set_data_json_approved_true_if_present(path: Path) -> bool:
|
|
|
528
527
|
# 가 whole-task 통합 실패를 동일 타입으로 받는다. run.py 는 재노출만 한다.
|
|
529
528
|
PrepareError = _stage_targets.PrepareError
|
|
530
529
|
from .phases.implementation_option_selection.entry import (
|
|
531
|
-
|
|
530
|
+
validate_brief_requirements,
|
|
532
531
|
)
|
|
533
532
|
|
|
534
533
|
|
|
@@ -557,7 +556,6 @@ class PrepareInputs:
|
|
|
557
556
|
evidence_inputs_raw: str = ""
|
|
558
557
|
user_authorization_json: str = ""
|
|
559
558
|
directive: str = ""
|
|
560
|
-
workers_override: str = ""
|
|
561
559
|
role_counts_raw: tuple[str, ...] = ()
|
|
562
560
|
role_models_raw: tuple[str, ...] = ()
|
|
563
561
|
host_session_context: HostSessionContext = field(
|
|
@@ -1103,31 +1101,6 @@ def _record_artifact_runtime_render_only_event(inp: PrepareInputs, ctx: dict) ->
|
|
|
1103
1101
|
)
|
|
1104
1102
|
|
|
1105
1103
|
|
|
1106
|
-
def _workers_flag_value(roster_csv: str) -> str:
|
|
1107
|
-
"""로스터 CSV 를 `--workers` 가 받는 어휘(provider id)로 되돌린다.
|
|
1108
|
-
|
|
1109
|
-
두 어휘가 다르다. 로스터 행은 슬롯 id(`codex-verifier`)이고 `--workers` 는
|
|
1110
|
-
프로파일의 `Required workers:` 가 선언한 **provider 허용목록**이다
|
|
1111
|
-
([`workers.py`](workers.py)). 로스터를 그대로 실으면 재실행 argv 가
|
|
1112
|
-
`unknown workers: codex-verifier` 로 죽는다 — 이 인자를 만드는 곳은 여기
|
|
1113
|
-
하나뿐이므로 변환도 여기서 한다.
|
|
1114
|
-
|
|
1115
|
-
같은 provider 의 슬롯 둘은 한 항목으로 접힌다. 슬롯 수는 이 인자가 아니라
|
|
1116
|
-
함께 실리는 `--role-count` · `--role-model` 이 나른다.
|
|
1117
|
-
"""
|
|
1118
|
-
seen: set[str] = set()
|
|
1119
|
-
out: list[str] = []
|
|
1120
|
-
for token in (part.strip() for part in roster_csv.split(",")):
|
|
1121
|
-
if not token:
|
|
1122
|
-
continue
|
|
1123
|
-
provider = worker_provider_id(token)
|
|
1124
|
-
if provider in seen:
|
|
1125
|
-
continue
|
|
1126
|
-
seen.add(provider)
|
|
1127
|
-
out.append(provider)
|
|
1128
|
-
return ",".join(out)
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
1104
|
def _canonical_argv(inp: PrepareInputs, ctx: dict) -> list[str]:
|
|
1132
1105
|
"""rerun 충실 재현을 위한 canonical argv 재구성."""
|
|
1133
1106
|
has_role_selection = bool(inp.role_counts_raw or inp.role_models_raw)
|
|
@@ -1136,9 +1109,6 @@ def _canonical_argv(inp: PrepareInputs, ctx: dict) -> list[str]:
|
|
|
1136
1109
|
{} if inp.host_session_context.entry_mode == "current-session"
|
|
1137
1110
|
else legacy_fallbacks
|
|
1138
1111
|
)
|
|
1139
|
-
workers = inp.workers_override or _workers_flag_value(
|
|
1140
|
-
legacy_fallbacks.get("RECOMMENDED_ANALYSERS", "")
|
|
1141
|
-
)
|
|
1142
1112
|
pairs = [
|
|
1143
1113
|
("--task-type", inp.task_type),
|
|
1144
1114
|
("--project-id", inp.project_id),
|
|
@@ -1152,7 +1122,6 @@ def _canonical_argv(inp: PrepareInputs, ctx: dict) -> list[str]:
|
|
|
1152
1122
|
("--implementation-option", inp.implementation_option),
|
|
1153
1123
|
("--clarification-response", inp.clarification_response_path),
|
|
1154
1124
|
("--selected-direction", inp.selected_direction_path),
|
|
1155
|
-
("--workers", workers),
|
|
1156
1125
|
(
|
|
1157
1126
|
"--lead-provider",
|
|
1158
1127
|
inp.lead_provider or lead_fallbacks.get("LEAD_PROVIDER", ""),
|
|
@@ -1161,19 +1130,9 @@ def _canonical_argv(inp: PrepareInputs, ctx: dict) -> list[str]:
|
|
|
1161
1130
|
"--lead-model",
|
|
1162
1131
|
inp.lead_model or lead_fallbacks.get("LEAD_MODEL", ""),
|
|
1163
1132
|
),
|
|
1164
|
-
(
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
),
|
|
1168
|
-
(
|
|
1169
|
-
"--codex-model",
|
|
1170
|
-
inp.codex_model or legacy_fallbacks.get("CODEX_WORKER_MODEL", ""),
|
|
1171
|
-
),
|
|
1172
|
-
(
|
|
1173
|
-
"--antigravity-model",
|
|
1174
|
-
inp.antigravity_model
|
|
1175
|
-
or legacy_fallbacks.get("ANTIGRAVITY_WORKER_MODEL", ""),
|
|
1176
|
-
),
|
|
1133
|
+
("--claude-model", inp.claude_model),
|
|
1134
|
+
("--codex-model", inp.codex_model),
|
|
1135
|
+
("--antigravity-model", inp.antigravity_model),
|
|
1177
1136
|
("--worker-model", inp.worker_models_raw),
|
|
1178
1137
|
(
|
|
1179
1138
|
"--report-writer-provider",
|
|
@@ -1817,36 +1776,6 @@ def _register_and_check_project(project_root: Path, inp: PrepareInputs) -> None:
|
|
|
1817
1776
|
_apply_tdd_bypass_if_requested(inp, project_root)
|
|
1818
1777
|
|
|
1819
1778
|
|
|
1820
|
-
def _resolve_roster(inp: PrepareInputs, profile_file: Path) -> tuple[list[str], str]:
|
|
1821
|
-
"""profile 의 `Required workers:` 블록을 권위로 roster 를 해소한다.
|
|
1822
|
-
|
|
1823
|
-
release-handoff 은 의도적으로 single-lead (worker dispatch / teammate /
|
|
1824
|
-
convergence 없음). profile 에 `- Required workers:` 블록이 없으므로 어떤
|
|
1825
|
-
override 가 와도 빈 roster 를 강제해 profile 계약과 일관성을 유지한다."""
|
|
1826
|
-
if inp.task_type == "release-handoff":
|
|
1827
|
-
return [], ""
|
|
1828
|
-
profile_workers = resolve_profile_workers(profile_file)
|
|
1829
|
-
optional_workers = resolve_optional_workers(profile_file)
|
|
1830
|
-
profile_workers_csv = ",".join(profile_workers)
|
|
1831
|
-
workers = normalize_workers(inp.workers_override or profile_workers_csv)
|
|
1832
|
-
if inp.workers_override.strip():
|
|
1833
|
-
validate_workers_against_profile(workers, profile_workers, optional_workers)
|
|
1834
|
-
try:
|
|
1835
|
-
role_profile = load_role_profile(profile_file)
|
|
1836
|
-
except RoleProfileError as exc:
|
|
1837
|
-
raise PrepareError(str(exc)) from exc
|
|
1838
|
-
requires_report_writer = any(
|
|
1839
|
-
row.role == "report-writer" and row.min_count == 1
|
|
1840
|
-
for row in role_profile.roles
|
|
1841
|
-
)
|
|
1842
|
-
if requires_report_writer and "report-writer" not in workers:
|
|
1843
|
-
workers.append("report-writer")
|
|
1844
|
-
if not workers:
|
|
1845
|
-
raise PrepareError(f"no workers resolved for profile: {inp.task_type}")
|
|
1846
|
-
_validate_option_selection_roster(inp.task_type, workers)
|
|
1847
|
-
return workers, ",".join(workers)
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
1779
|
def _profile_requires_executor(profile_file: Path) -> bool:
|
|
1851
1780
|
try:
|
|
1852
1781
|
profile = load_role_profile(profile_file)
|
|
@@ -1865,21 +1794,10 @@ def _normalize_prepare_model_selection(
|
|
|
1865
1794
|
try:
|
|
1866
1795
|
_validate_critic_choice(inp.critic)
|
|
1867
1796
|
profile = load_role_profile(profile_file)
|
|
1868
|
-
profile_workers = resolve_profile_workers(profile_file)
|
|
1869
|
-
has_worker_models = bool(
|
|
1870
|
-
inp.worker_models_raw.strip()
|
|
1871
|
-
or inp.claude_model.strip()
|
|
1872
|
-
or inp.codex_model.strip()
|
|
1873
|
-
or inp.antigravity_model.strip()
|
|
1874
|
-
)
|
|
1875
|
-
workers = inp.workers_override
|
|
1876
|
-
if not workers and has_worker_models:
|
|
1877
|
-
workers = ",".join(profile_workers)
|
|
1878
1797
|
selection = normalize_model_selection_inputs(
|
|
1879
1798
|
profile=profile,
|
|
1880
1799
|
role_counts_raw=inp.role_counts_raw,
|
|
1881
1800
|
role_models_raw=inp.role_models_raw,
|
|
1882
|
-
workers=workers,
|
|
1883
1801
|
worker_model=inp.worker_models_raw,
|
|
1884
1802
|
critic=inp.critic,
|
|
1885
1803
|
executor=inp.executor,
|
|
@@ -1925,7 +1843,6 @@ def _resolve_prepare_assignments(
|
|
|
1925
1843
|
inp: PrepareInputs,
|
|
1926
1844
|
profile: RoleProfile,
|
|
1927
1845
|
selection: CanonicalModelSelection,
|
|
1928
|
-
workers: list[str],
|
|
1929
1846
|
lead_runtime: str,
|
|
1930
1847
|
terminal_backend: TerminalBackend,
|
|
1931
1848
|
requires_executor: bool,
|
|
@@ -1935,7 +1852,6 @@ def _resolve_prepare_assignments(
|
|
|
1935
1852
|
inp,
|
|
1936
1853
|
profile,
|
|
1937
1854
|
selection,
|
|
1938
|
-
workers,
|
|
1939
1855
|
scopes,
|
|
1940
1856
|
requires_executor,
|
|
1941
1857
|
)
|
|
@@ -2078,7 +1994,6 @@ def _selection_provider_ids(
|
|
|
2078
1994
|
inp: PrepareInputs,
|
|
2079
1995
|
profile: RoleProfile,
|
|
2080
1996
|
selection: CanonicalModelSelection,
|
|
2081
|
-
workers: list[str],
|
|
2082
1997
|
scopes: ModelDefaultScopes,
|
|
2083
1998
|
requires_executor: bool,
|
|
2084
1999
|
) -> tuple[str, ...]:
|
|
@@ -2095,15 +2010,21 @@ def _selection_provider_ids(
|
|
|
2095
2010
|
inp,
|
|
2096
2011
|
profile,
|
|
2097
2012
|
selection,
|
|
2098
|
-
workers,
|
|
2099
2013
|
scopes,
|
|
2100
2014
|
default_roles,
|
|
2101
2015
|
requires_executor,
|
|
2102
2016
|
)
|
|
2103
2017
|
else:
|
|
2104
2018
|
selected = list(
|
|
2105
|
-
_selected_execution_provider_ids(inp,
|
|
2019
|
+
_selected_execution_provider_ids(inp, requires_executor)
|
|
2106
2020
|
)
|
|
2021
|
+
# 스냅샷에 없는 공급자의 모델은 번들 기본값 후보에서 빠진다. 사용자가 고르지
|
|
2022
|
+
# 않은 분석 칸은 등록 순서의 분석 공급자를 칸 수만큼 후보에 둔다.
|
|
2023
|
+
selected.extend(
|
|
2024
|
+
provider_ids("analyser")[:_bundled_analysis_default_slots(
|
|
2025
|
+
profile, selection, scopes,
|
|
2026
|
+
)]
|
|
2027
|
+
)
|
|
2107
2028
|
selected.extend(
|
|
2108
2029
|
provider
|
|
2109
2030
|
for providers in selection.provider_constraints.values()
|
|
@@ -2173,7 +2094,6 @@ def _canonical_selection_provider_ids(
|
|
|
2173
2094
|
inp: PrepareInputs,
|
|
2174
2095
|
profile: RoleProfile,
|
|
2175
2096
|
selection: CanonicalModelSelection,
|
|
2176
|
-
workers: list[str],
|
|
2177
2097
|
scopes: ModelDefaultScopes,
|
|
2178
2098
|
default_roles: Sequence[str],
|
|
2179
2099
|
requires_executor: bool,
|
|
@@ -2195,27 +2115,24 @@ def _canonical_selection_provider_ids(
|
|
|
2195
2115
|
if implementer_uses_bundled_default:
|
|
2196
2116
|
# `--executor` names the provider that implements, so it belongs in the
|
|
2197
2117
|
# roster the same way the bundled default does. Reading only the default
|
|
2198
|
-
# left `--executor <provider>` with no assignment of its own
|
|
2199
|
-
# rendered without that provider, and asking for it with `--workers`
|
|
2200
|
-
# was refused as not being in the roster.
|
|
2118
|
+
# left `--executor <provider>` with no assignment of its own.
|
|
2201
2119
|
selected.append(
|
|
2202
2120
|
(inp.executor or "").strip().lower()
|
|
2203
2121
|
or _default("OKSTRA_DEFAULT_EXECUTOR", "claude")
|
|
2204
2122
|
)
|
|
2205
|
-
if _needs_profile_worker_candidates(profile, selection, scopes):
|
|
2206
|
-
selected.extend(
|
|
2207
|
-
worker for worker in workers if worker != "report-writer"
|
|
2208
|
-
)
|
|
2209
2123
|
return selected
|
|
2210
2124
|
|
|
2211
2125
|
|
|
2212
|
-
def
|
|
2126
|
+
def _bundled_analysis_default_slots(
|
|
2213
2127
|
profile: RoleProfile,
|
|
2214
2128
|
selection: CanonicalModelSelection,
|
|
2215
2129
|
scopes: ModelDefaultScopes,
|
|
2216
|
-
) ->
|
|
2130
|
+
) -> int:
|
|
2131
|
+
slots = 0
|
|
2217
2132
|
for requirement in profile.roles:
|
|
2218
|
-
if requirement.dynamic or requirement.role
|
|
2133
|
+
if requirement.dynamic or requirement.role in {
|
|
2134
|
+
"report-writer", "implementer", "critic",
|
|
2135
|
+
}:
|
|
2219
2136
|
continue
|
|
2220
2137
|
desired = selection.role_counts.get(
|
|
2221
2138
|
requirement.role,
|
|
@@ -2227,8 +2144,8 @@ def _needs_profile_worker_candidates(
|
|
|
2227
2144
|
or requirement.role in scopes.global_
|
|
2228
2145
|
)
|
|
2229
2146
|
if explicit < desired and not has_scoped_default:
|
|
2230
|
-
|
|
2231
|
-
return
|
|
2147
|
+
slots = max(slots, desired)
|
|
2148
|
+
return slots
|
|
2232
2149
|
|
|
2233
2150
|
|
|
2234
2151
|
def _providers_from_model_refs(
|
|
@@ -2400,13 +2317,6 @@ def _read_model_defaults(path: Path) -> dict[str, tuple[str, ...]]:
|
|
|
2400
2317
|
return defaults
|
|
2401
2318
|
|
|
2402
2319
|
|
|
2403
|
-
def _validate_option_selection_roster(task_type: str, workers: Sequence[str]) -> None:
|
|
2404
|
-
if task_type == "implementation-option-selection":
|
|
2405
|
-
validate_analyser_roster(workers)
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
2320
|
@dataclass
|
|
2411
2321
|
class RoleAssignment:
|
|
2412
2322
|
role: str
|
|
@@ -2530,29 +2440,6 @@ def recommended_role_models(
|
|
|
2530
2440
|
return recommendations
|
|
2531
2441
|
|
|
2532
2442
|
|
|
2533
|
-
def _parse_worker_model_overrides(
|
|
2534
|
-
raw_value: str,
|
|
2535
|
-
context: AssignmentContext,
|
|
2536
|
-
) -> dict[str, str]:
|
|
2537
|
-
overrides: dict[str, str] = {}
|
|
2538
|
-
for item in (raw_value or "").split(","):
|
|
2539
|
-
item = item.strip()
|
|
2540
|
-
if not item:
|
|
2541
|
-
continue
|
|
2542
|
-
provider, separator, model = item.partition("=")
|
|
2543
|
-
provider = provider.strip().lower()
|
|
2544
|
-
model = model.strip()
|
|
2545
|
-
if not separator or not provider or not model:
|
|
2546
|
-
raise PrepareError(
|
|
2547
|
-
"--worker-model must use provider=model entries separated by commas"
|
|
2548
|
-
)
|
|
2549
|
-
if provider in overrides:
|
|
2550
|
-
raise PrepareError(f"duplicate --worker-model provider: {provider}")
|
|
2551
|
-
context.pool.resolve_provider(provider)
|
|
2552
|
-
overrides[provider] = model
|
|
2553
|
-
return overrides
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
2443
|
def _model_for_role(
|
|
2557
2444
|
*,
|
|
2558
2445
|
context: AssignmentContext,
|
|
@@ -2575,209 +2462,6 @@ def _model_for_role(
|
|
|
2575
2462
|
raise PrepareError(str(exc)) from exc
|
|
2576
2463
|
|
|
2577
2464
|
|
|
2578
|
-
def _resolve_worker_models(
|
|
2579
|
-
inp: PrepareInputs,
|
|
2580
|
-
workers: list[str],
|
|
2581
|
-
context: AssignmentContext,
|
|
2582
|
-
) -> dict:
|
|
2583
|
-
"""Resolve provider models while preserving legacy fixed-provider projections."""
|
|
2584
|
-
overrides = _parse_worker_model_overrides(inp.worker_models_raw, context)
|
|
2585
|
-
legacy = {
|
|
2586
|
-
"claude": inp.claude_model,
|
|
2587
|
-
"codex": inp.codex_model,
|
|
2588
|
-
"antigravity": inp.antigravity_model,
|
|
2589
|
-
}
|
|
2590
|
-
legacy_default_env = {
|
|
2591
|
-
"claude": "OKSTRA_DEFAULT_CLAUDE_MODEL",
|
|
2592
|
-
"codex": "OKSTRA_DEFAULT_CODEX_MODEL",
|
|
2593
|
-
"antigravity": "OKSTRA_DEFAULT_ANTIGRAVITY_MODEL",
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
def worker_model_input(provider: str) -> str:
|
|
2597
|
-
selected = overrides.get(provider, legacy.get(provider, ""))
|
|
2598
|
-
env_name = legacy_default_env.get(provider)
|
|
2599
|
-
return selected or (_default(env_name, "") if env_name else "")
|
|
2600
|
-
|
|
2601
|
-
selected_worker_providers = tuple(dict.fromkeys(
|
|
2602
|
-
worker for worker in workers if worker != "report-writer"
|
|
2603
|
-
))
|
|
2604
|
-
provider_models = {}
|
|
2605
|
-
for provider in selected_worker_providers:
|
|
2606
|
-
provider_models[provider] = _model_for_role(
|
|
2607
|
-
context=context,
|
|
2608
|
-
provider=provider,
|
|
2609
|
-
raw_value=worker_model_input(provider),
|
|
2610
|
-
role="analyser",
|
|
2611
|
-
)
|
|
2612
|
-
try:
|
|
2613
|
-
lead_assignment = resolve_context_lead_provider(
|
|
2614
|
-
context=context,
|
|
2615
|
-
requested_provider=inp.lead_provider,
|
|
2616
|
-
)
|
|
2617
|
-
except (
|
|
2618
|
-
HostCapabilityMismatch,
|
|
2619
|
-
HostNotRegistered,
|
|
2620
|
-
ProviderUnavailable,
|
|
2621
|
-
UnknownProviderError,
|
|
2622
|
-
) as exc:
|
|
2623
|
-
raise PrepareError(str(exc)) from exc
|
|
2624
|
-
lead_provider = lead_assignment.provider
|
|
2625
|
-
report_writer_provider = inp.report_writer_provider.strip().lower() or "claude"
|
|
2626
|
-
if not context.pool.resolve_provider(report_writer_provider).supports_role(
|
|
2627
|
-
"report-writer"
|
|
2628
|
-
):
|
|
2629
|
-
raise PrepareError(
|
|
2630
|
-
f"provider {report_writer_provider!r} does not support the report-writer role"
|
|
2631
|
-
)
|
|
2632
|
-
|
|
2633
|
-
def legacy_projection(provider: str) -> _LegacyModelProjection:
|
|
2634
|
-
assigned = provider_models.get(provider)
|
|
2635
|
-
if assigned is not None:
|
|
2636
|
-
return _LegacyModelProjection(
|
|
2637
|
-
model_id=assigned.model_id,
|
|
2638
|
-
display=assigned.display,
|
|
2639
|
-
execution=assigned.execution,
|
|
2640
|
-
)
|
|
2641
|
-
try:
|
|
2642
|
-
selected = resolve_model_selection(
|
|
2643
|
-
context.pool,
|
|
2644
|
-
provider,
|
|
2645
|
-
"analyser",
|
|
2646
|
-
worker_model_input(provider),
|
|
2647
|
-
)
|
|
2648
|
-
except (ValueError, RuntimeError) as exc:
|
|
2649
|
-
raise PrepareError(str(exc)) from exc
|
|
2650
|
-
return _LegacyModelProjection(
|
|
2651
|
-
model_id=selected.model_ref.model_id,
|
|
2652
|
-
display=selected.display_name,
|
|
2653
|
-
execution=selected.execution_value,
|
|
2654
|
-
)
|
|
2655
|
-
|
|
2656
|
-
return {
|
|
2657
|
-
"lead_provider": lead_provider,
|
|
2658
|
-
"report_writer_provider": report_writer_provider,
|
|
2659
|
-
"lead": _model_for_role(
|
|
2660
|
-
context=context,
|
|
2661
|
-
provider=lead_provider,
|
|
2662
|
-
raw_value=inp.lead_model,
|
|
2663
|
-
role="leader",
|
|
2664
|
-
default_override=_legacy_claude_model_override(
|
|
2665
|
-
lead_provider,
|
|
2666
|
-
"OKSTRA_DEFAULT_LEAD_MODEL",
|
|
2667
|
-
),
|
|
2668
|
-
duty_id="lead",
|
|
2669
|
-
),
|
|
2670
|
-
"cw": legacy_projection("claude"),
|
|
2671
|
-
"co": legacy_projection("codex"),
|
|
2672
|
-
"ge": legacy_projection("antigravity"),
|
|
2673
|
-
"rw": _model_for_role(
|
|
2674
|
-
context=context,
|
|
2675
|
-
provider=report_writer_provider,
|
|
2676
|
-
raw_value=inp.report_writer_model,
|
|
2677
|
-
role="report-writer",
|
|
2678
|
-
default_override=_legacy_claude_model_override(
|
|
2679
|
-
report_writer_provider,
|
|
2680
|
-
"OKSTRA_DEFAULT_REPORT_WRITER_MODEL",
|
|
2681
|
-
),
|
|
2682
|
-
),
|
|
2683
|
-
"providers": provider_models,
|
|
2684
|
-
"worker_model_inputs": {
|
|
2685
|
-
provider: worker_model_input(provider)
|
|
2686
|
-
for provider in dict.fromkeys((
|
|
2687
|
-
*selected_worker_providers,
|
|
2688
|
-
*overrides,
|
|
2689
|
-
*legacy,
|
|
2690
|
-
))
|
|
2691
|
-
},
|
|
2692
|
-
}
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
def _resolve_model_bindings(
|
|
2696
|
-
inp: PrepareInputs,
|
|
2697
|
-
workers: list[str],
|
|
2698
|
-
context: AssignmentContext,
|
|
2699
|
-
requires_executor: bool,
|
|
2700
|
-
) -> _ModelBindings:
|
|
2701
|
-
"""worker 모델 + critic 선택 + executor 바인딩을 한 묶음으로 해소·검증한다."""
|
|
2702
|
-
m = _resolve_worker_models(inp, workers, context)
|
|
2703
|
-
critic_choice = _validate_critic_choice(inp.critic)
|
|
2704
|
-
critic_model_execution = ""
|
|
2705
|
-
critic_meta = None
|
|
2706
|
-
if critic_choice in provider_ids("critic"):
|
|
2707
|
-
critic_meta = _model_for_role(
|
|
2708
|
-
context=context,
|
|
2709
|
-
provider=critic_choice,
|
|
2710
|
-
raw_value=m["worker_model_inputs"].get(critic_choice, ""),
|
|
2711
|
-
role="critic",
|
|
2712
|
-
duty_id="scope-critic",
|
|
2713
|
-
)
|
|
2714
|
-
critic_model_execution = critic_meta.execution
|
|
2715
|
-
executor_assignment = None
|
|
2716
|
-
display_name = ""
|
|
2717
|
-
if requires_executor:
|
|
2718
|
-
executor_assignment, display_name = _resolve_executor_assignment(
|
|
2719
|
-
inp,
|
|
2720
|
-
workers,
|
|
2721
|
-
context,
|
|
2722
|
-
m["worker_model_inputs"],
|
|
2723
|
-
)
|
|
2724
|
-
lead_assignment = _role_assignment(
|
|
2725
|
-
resolved=m["lead"],
|
|
2726
|
-
role="lead",
|
|
2727
|
-
)
|
|
2728
|
-
worker_assignments = _build_worker_assignments(workers, m)
|
|
2729
|
-
critic_assignment = (
|
|
2730
|
-
_role_assignment(
|
|
2731
|
-
resolved=critic_meta,
|
|
2732
|
-
role="critic",
|
|
2733
|
-
)
|
|
2734
|
-
if critic_meta is not None
|
|
2735
|
-
else None
|
|
2736
|
-
)
|
|
2737
|
-
translator_meta = _model_for_role(
|
|
2738
|
-
context=context,
|
|
2739
|
-
provider=m["report_writer_provider"],
|
|
2740
|
-
raw_value=m["rw"].model_id,
|
|
2741
|
-
role="translator",
|
|
2742
|
-
duty_id="translator",
|
|
2743
|
-
)
|
|
2744
|
-
translator_assignment = _role_assignment(
|
|
2745
|
-
resolved=translator_meta,
|
|
2746
|
-
role="translator",
|
|
2747
|
-
)
|
|
2748
|
-
invocation_assignments = _build_invocation_assignments(
|
|
2749
|
-
lead_assignment,
|
|
2750
|
-
worker_assignments,
|
|
2751
|
-
critic_assignment,
|
|
2752
|
-
translator_assignment,
|
|
2753
|
-
inp.task_type,
|
|
2754
|
-
)
|
|
2755
|
-
worker_by_provider = {
|
|
2756
|
-
row.provider: row for row in worker_assignments
|
|
2757
|
-
}
|
|
2758
|
-
return _ModelBindings(
|
|
2759
|
-
lead=m["lead"], cw=m["cw"], co=m["co"], ge=m["ge"], rw=m["rw"],
|
|
2760
|
-
critic_choice=critic_choice,
|
|
2761
|
-
critic_model_execution=critic_model_execution,
|
|
2762
|
-
executor_display_name=display_name,
|
|
2763
|
-
codex_worker_execution=(
|
|
2764
|
-
worker_by_provider["codex"].model_execution_value
|
|
2765
|
-
if "codex" in worker_by_provider
|
|
2766
|
-
else m["co"].execution
|
|
2767
|
-
),
|
|
2768
|
-
antigravity_worker_execution=(
|
|
2769
|
-
worker_by_provider["antigravity"].model_execution_value
|
|
2770
|
-
if "antigravity" in worker_by_provider
|
|
2771
|
-
else m["ge"].execution
|
|
2772
|
-
),
|
|
2773
|
-
executor_assignment=executor_assignment,
|
|
2774
|
-
lead_assignment=lead_assignment,
|
|
2775
|
-
worker_assignments=worker_assignments,
|
|
2776
|
-
invocation_assignments=invocation_assignments,
|
|
2777
|
-
translator=translator_meta,
|
|
2778
|
-
)
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
2465
|
def _validate_critic_choice(raw_value: str) -> str:
|
|
2782
2466
|
"""critic 은 어느 phase 에서도 opt-in 이다 — 프로필이 `min: 0` 으로 선언한다.
|
|
2783
2467
|
|
|
@@ -3006,12 +2690,10 @@ def _legacy_projection_from_plan(
|
|
|
3006
2690
|
|
|
3007
2691
|
def _selected_execution_provider_ids(
|
|
3008
2692
|
inp: PrepareInputs,
|
|
3009
|
-
workers: list[str],
|
|
3010
2693
|
requires_executor: bool,
|
|
3011
2694
|
) -> tuple[str, ...]:
|
|
3012
2695
|
selected = [
|
|
3013
2696
|
inp.lead_provider,
|
|
3014
|
-
*(worker for worker in workers if worker != "report-writer"),
|
|
3015
2697
|
inp.report_writer_provider.strip().lower() or "claude",
|
|
3016
2698
|
]
|
|
3017
2699
|
critic = inp.critic.strip().lower()
|
|
@@ -3025,64 +2707,6 @@ def _selected_execution_provider_ids(
|
|
|
3025
2707
|
return tuple(dict.fromkeys(selected))
|
|
3026
2708
|
|
|
3027
2709
|
|
|
3028
|
-
def _resolve_executor_assignment(
|
|
3029
|
-
inp: PrepareInputs,
|
|
3030
|
-
workers: list[str],
|
|
3031
|
-
context: AssignmentContext,
|
|
3032
|
-
worker_model_inputs: dict[str, str],
|
|
3033
|
-
) -> tuple[RoleAssignment, str]:
|
|
3034
|
-
executor_default = _default("OKSTRA_DEFAULT_EXECUTOR", "claude")
|
|
3035
|
-
executor_provider = (inp.executor or executor_default).strip().lower()
|
|
3036
|
-
allowed_executors = provider_ids("executor")
|
|
3037
|
-
if executor_provider not in allowed_executors:
|
|
3038
|
-
raise PrepareError(
|
|
3039
|
-
f"--executor must be one of: {', '.join(allowed_executors)} "
|
|
3040
|
-
f"(got: {executor_provider!r})"
|
|
3041
|
-
)
|
|
3042
|
-
if executor_provider not in workers:
|
|
3043
|
-
raise PrepareError(
|
|
3044
|
-
f"--executor {executor_provider} requires {executor_provider!r} in "
|
|
3045
|
-
f"--workers, but resolved roster is {workers!r}. "
|
|
3046
|
-
f"Add it explicitly, e.g. --workers {','.join(sorted(set(workers + [executor_provider])))}."
|
|
3047
|
-
)
|
|
3048
|
-
model = _model_for_role(
|
|
3049
|
-
context=context,
|
|
3050
|
-
provider=executor_provider,
|
|
3051
|
-
raw_value=worker_model_inputs.get(executor_provider, ""),
|
|
3052
|
-
role="implementer",
|
|
3053
|
-
duty_id="implementation-executor",
|
|
3054
|
-
)
|
|
3055
|
-
assignment = _role_assignment(
|
|
3056
|
-
resolved=model,
|
|
3057
|
-
role="executor",
|
|
3058
|
-
worker_id=executor_provider,
|
|
3059
|
-
)
|
|
3060
|
-
display_name = (
|
|
3061
|
-
f"{context.pool.resolve_provider(executor_provider).display_label} executor"
|
|
3062
|
-
)
|
|
3063
|
-
return assignment, display_name
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
def _build_worker_assignments(
|
|
3067
|
-
workers: list[str], models: dict,
|
|
3068
|
-
) -> tuple[RoleAssignment, ...]:
|
|
3069
|
-
assignments: list[RoleAssignment] = []
|
|
3070
|
-
for worker_id in workers:
|
|
3071
|
-
is_report_writer = worker_id == "report-writer"
|
|
3072
|
-
provider = (
|
|
3073
|
-
models["report_writer_provider"] if is_report_writer else worker_id
|
|
3074
|
-
)
|
|
3075
|
-
selected = (
|
|
3076
|
-
models["rw"] if is_report_writer else models["providers"][provider]
|
|
3077
|
-
)
|
|
3078
|
-
assignments.append(_role_assignment(
|
|
3079
|
-
resolved=selected,
|
|
3080
|
-
role="report-writer" if is_report_writer else "analyser",
|
|
3081
|
-
worker_id=worker_id,
|
|
3082
|
-
))
|
|
3083
|
-
return tuple(assignments)
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
2710
|
def _role_assignment(
|
|
3087
2711
|
*,
|
|
3088
2712
|
resolved: ResolvedAssignment,
|
|
@@ -3138,7 +2762,7 @@ def _build_invocation_assignments(
|
|
|
3138
2762
|
)
|
|
3139
2763
|
if critic is not None:
|
|
3140
2764
|
# 한 run 이 도달할 수 있는 critic 은 한 종류다. 둘 다 등록하면 실행될
|
|
3141
|
-
# 수 없는 로스터 행이 남고, `
|
|
2765
|
+
# 수 없는 로스터 행이 남고, `_critic_role_rows` 가 provider 로만
|
|
3142
2766
|
# role 라벨을 만들기 때문에 두 행의 이름이 겹쳐 validate-run 이
|
|
3143
2767
|
# `duplicate worker role detected` 로 런을 실패시킨다.
|
|
3144
2768
|
assignments[critic_assignment_ref(task_type)] = critic.to_model_payload()
|
|
@@ -3404,11 +3028,19 @@ def _write_instruction_set_sources(
|
|
|
3404
3028
|
if inp.task_type == "implementation-planning" and inp.selected_direction_path
|
|
3405
3029
|
else ""
|
|
3406
3030
|
)
|
|
3031
|
+
from .business_flow.hooks import prepare_task_knowledge
|
|
3032
|
+
|
|
3033
|
+
business_knowledge = prepare_task_knowledge(
|
|
3034
|
+
Path(ctx["PROJECT_ROOT"]), instruction_set, ctx,
|
|
3035
|
+
(instruction_set / "task-brief.md").read_text(encoding="utf-8") + "\n" + inp.directive,
|
|
3036
|
+
)
|
|
3037
|
+
coverage_census_text = _write_coverage_census(inp, ctx, instruction_set)
|
|
3407
3038
|
packet = build_analysis_packet(
|
|
3408
3039
|
task_key=ctx["TASK_KEY"],
|
|
3409
3040
|
task_type=ctx["TASK_TYPE"],
|
|
3410
3041
|
task_brief_path=instruction_set / "task-brief.md",
|
|
3411
3042
|
group_context_path=group_context_path,
|
|
3043
|
+
business_knowledge_text=business_knowledge,
|
|
3412
3044
|
analysis_profile_path=instruction_set / "analysis-profile.md",
|
|
3413
3045
|
reference_expectations_path=instruction_set / "reference-expectations.md",
|
|
3414
3046
|
clarification_response_path=(
|
|
@@ -3426,6 +3058,7 @@ def _write_instruction_set_sources(
|
|
|
3426
3058
|
Path(ctx["PROJECT_ROOT"]), ctx["TASK_KEY"],
|
|
3427
3059
|
json.loads(ctx.get("RELATED_TASKS_JSON", "[]")),
|
|
3428
3060
|
),
|
|
3061
|
+
coverage_census_text=coverage_census_text,
|
|
3429
3062
|
)
|
|
3430
3063
|
if inp.task_type == "technical-verification":
|
|
3431
3064
|
packet += materialize_technical_input_packet(
|
|
@@ -3441,6 +3074,46 @@ def _write_instruction_set_sources(
|
|
|
3441
3074
|
return instruction_set
|
|
3442
3075
|
|
|
3443
3076
|
|
|
3077
|
+
def _write_coverage_census(
|
|
3078
|
+
inp: PrepareInputs, ctx: dict, instruction_set: Path,
|
|
3079
|
+
) -> str:
|
|
3080
|
+
"""Persist this run's coverage census and return its packet block.
|
|
3081
|
+
|
|
3082
|
+
The run-scoped state file is the canonical copy because the packet lives in
|
|
3083
|
+
the task-scoped instruction set and the next phase overwrites it. A profile
|
|
3084
|
+
block that cannot be read is reported and the run goes on without a census.
|
|
3085
|
+
"""
|
|
3086
|
+
changed_files: tuple[str, ...] = ()
|
|
3087
|
+
if inp.task_type == "final-verification":
|
|
3088
|
+
changed_files = tuple(_git_out(
|
|
3089
|
+
ctx["VERIFICATION_WORKTREE_PATH"],
|
|
3090
|
+
"diff",
|
|
3091
|
+
"--name-only",
|
|
3092
|
+
f"{ctx['VERIFICATION_BASE_REF']}..{ctx['VERIFICATION_HEAD_REF']}",
|
|
3093
|
+
).splitlines())
|
|
3094
|
+
try:
|
|
3095
|
+
census = census_for_run(
|
|
3096
|
+
task_type=inp.task_type,
|
|
3097
|
+
profile_text=(instruction_set / "analysis-profile.md").read_text(
|
|
3098
|
+
encoding="utf-8"
|
|
3099
|
+
),
|
|
3100
|
+
brief_path=instruction_set / "task-brief.md",
|
|
3101
|
+
changed_files=changed_files,
|
|
3102
|
+
)
|
|
3103
|
+
except CensusError as exc:
|
|
3104
|
+
print(f"prepare: coverage census skipped: {exc}", file=sys.stderr)
|
|
3105
|
+
return ""
|
|
3106
|
+
if census is None:
|
|
3107
|
+
return ""
|
|
3108
|
+
state_dir = Path(ctx["RUN_STATE_DIR"])
|
|
3109
|
+
state_dir.mkdir(parents=True, exist_ok=True)
|
|
3110
|
+
census_path = state_dir / census_filename(inp.task_type, ctx["RUN_STATE_SEQ"])
|
|
3111
|
+
write_owned_object_atomic(census_path, census, artifact="coverage census")
|
|
3112
|
+
return render_census_section(
|
|
3113
|
+
census, relative_to_project_root(census_path, Path(ctx["PROJECT_ROOT"]))
|
|
3114
|
+
)
|
|
3115
|
+
|
|
3116
|
+
|
|
3444
3117
|
def _write_analysis_evidence_artifact(ctx: dict, instruction_set: Path) -> None:
|
|
3445
3118
|
"""Write selected evidence metadata without copying the original reports."""
|
|
3446
3119
|
if ctx.get("TASK_TYPE") not in ANALYSIS_TASK_TYPES:
|
|
@@ -4436,7 +4109,6 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
4436
4109
|
handoff_tokens = materialize_handoff_input(
|
|
4437
4110
|
workspace_root, project_root, inp, install_hint=_INSTALL_HINT)
|
|
4438
4111
|
|
|
4439
|
-
workers, selected_reviewers = _resolve_roster(inp, profile_file)
|
|
4440
4112
|
pr_template_path_str, pr_template_source = (
|
|
4441
4113
|
resolve_pr_template(inp) if inp.task_type == "release-handoff" else ("", "")
|
|
4442
4114
|
)
|
|
@@ -4445,7 +4117,6 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
4445
4117
|
inp=inp,
|
|
4446
4118
|
profile=role_profile,
|
|
4447
4119
|
selection=selection,
|
|
4448
|
-
workers=workers,
|
|
4449
4120
|
lead_runtime=lead_runtime,
|
|
4450
4121
|
terminal_backend=terminal_backend,
|
|
4451
4122
|
requires_executor=requires_executor,
|
|
@@ -4805,7 +4476,7 @@ bundle, and prints the rendered lead prompt without dispatching workers.
|
|
|
4805
4476
|
Usage:
|
|
4806
4477
|
okstra codex-run --project-root <dir> --project-id <id> \
|
|
4807
4478
|
--task-group <tg> --task-id <tid> --task-type <type> \
|
|
4808
|
-
--task-brief <path-or-rel> [--
|
|
4479
|
+
--task-brief <path-or-rel> [--directive <text>] \
|
|
4809
4480
|
[--lead-model <m>] [--claude-model <m>] [--codex-model <m>] \
|
|
4810
4481
|
[--antigravity-model <m>] [--report-writer-model <m>] \
|
|
4811
4482
|
[--related-tasks <list>] [--base-ref <ref>] \
|
|
@@ -4855,7 +4526,6 @@ def build_prepare_argument_parser():
|
|
|
4855
4526
|
"open + run 부착, 'no'/'' = 기록 안 함."
|
|
4856
4527
|
),
|
|
4857
4528
|
)
|
|
4858
|
-
p.add_argument("--workers", default="", dest="workers_override")
|
|
4859
4529
|
p.add_argument("--role-count", action="append", default=[], dest="role_counts_raw")
|
|
4860
4530
|
p.add_argument("--role-model", action="append", default=[], dest="role_models_raw")
|
|
4861
4531
|
p.add_argument(
|
|
@@ -5131,7 +4801,6 @@ def main(argv: list[str]) -> int:
|
|
|
5131
4801
|
evidence_inputs_raw=args.evidence_inputs_raw,
|
|
5132
4802
|
directive=args.directive,
|
|
5133
4803
|
user_authorization_json=args.user_authorization_json,
|
|
5134
|
-
workers_override=args.workers_override,
|
|
5135
4804
|
role_counts_raw=tuple(args.role_counts_raw),
|
|
5136
4805
|
role_models_raw=tuple(args.role_models_raw),
|
|
5137
4806
|
host_session_context=host_session_context,
|