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
|
@@ -23,7 +23,6 @@ from .ids import (
|
|
|
23
23
|
PICK_TYPE_CUSTOM,
|
|
24
24
|
S_ANALYSIS_TARGET,
|
|
25
25
|
S_ANALYSIS_TARGET_PICK,
|
|
26
|
-
S_ANTIGRAVITY_MODEL,
|
|
27
26
|
S_APPROVED_PLAN,
|
|
28
27
|
S_APPROVED_PLAN_PICK,
|
|
29
28
|
S_APPROVE_PLAN_CONFIRM,
|
|
@@ -35,27 +34,17 @@ from .ids import (
|
|
|
35
34
|
S_BRIEF_PATH_PICK,
|
|
36
35
|
S_CLARIFICATION,
|
|
37
36
|
S_CLARIFICATION_PICK,
|
|
38
|
-
S_CLAUDE_MODEL,
|
|
39
|
-
S_CODEX_MODEL,
|
|
40
37
|
S_CONFIRM,
|
|
41
|
-
S_CRITIC_PICK,
|
|
42
|
-
S_CRITIC_TEXT,
|
|
43
|
-
S_DEFAULTS_OR_CUSTOM,
|
|
44
38
|
S_DESIGN_PREP_CONFIRM,
|
|
45
39
|
S_DESIGN_PREP_DECISION,
|
|
46
40
|
S_DESIGN_PREP_OVERRIDES,
|
|
47
41
|
S_DIRECTIVE,
|
|
48
42
|
S_DIRECTIVE_PICK,
|
|
49
43
|
S_EDIT_TARGET,
|
|
50
|
-
S_EXECUTOR,
|
|
51
|
-
S_EXECUTOR_MODEL,
|
|
52
44
|
S_FEATURE_EVIDENCE,
|
|
53
45
|
S_FEATURE_EVIDENCE_PICK,
|
|
54
46
|
S_FIX_CYCLE_CONFIRM,
|
|
55
|
-
S_GROK_MODEL,
|
|
56
47
|
S_HANDOFF_STAGE_PICK,
|
|
57
|
-
S_KIMI_MODEL,
|
|
58
|
-
S_LEAD_MODEL,
|
|
59
48
|
S_PROJECT_EVIDENCE,
|
|
60
49
|
S_PROJECT_EVIDENCE_PICK,
|
|
61
50
|
S_PR_TEMPLATE,
|
|
@@ -63,7 +52,6 @@ from .ids import (
|
|
|
63
52
|
S_PR_TEMPLATE_SCOPE,
|
|
64
53
|
S_RELATED_TASKS,
|
|
65
54
|
S_RELATED_TASKS_PICK,
|
|
66
|
-
S_REPORT_WRITER_MODEL,
|
|
67
55
|
S_REPORT_LANGUAGE,
|
|
68
56
|
S_REUSE_PREVIOUS,
|
|
69
57
|
S_REVERIFY_SCOPE_PICK,
|
|
@@ -77,8 +65,6 @@ from .ids import (
|
|
|
77
65
|
S_TASK_PICK,
|
|
78
66
|
S_TASK_TYPE,
|
|
79
67
|
S_TASK_TYPE_TEXT,
|
|
80
|
-
S_WORKERS_CUSTOM,
|
|
81
|
-
S_WORKERS_OVERRIDE,
|
|
82
68
|
_BRIEF_ENTRY_TASK_TYPES,
|
|
83
69
|
_RESUME_REUSE_PHASES,
|
|
84
70
|
_STAGE_SCOPED_TASK_TYPES,
|
|
@@ -94,14 +80,12 @@ from .state import (
|
|
|
94
80
|
_identity_ready,
|
|
95
81
|
_is_role_selection_step,
|
|
96
82
|
_reset_field,
|
|
97
|
-
_role_selection_enabled,
|
|
98
83
|
)
|
|
99
84
|
from .prompts import _opt, _p
|
|
100
85
|
from .sources import (
|
|
101
86
|
_brief_resolved,
|
|
102
87
|
_has_prior_run_inputs,
|
|
103
88
|
_latest_implementation_planning_report,
|
|
104
|
-
_resolved_roster,
|
|
105
89
|
_reverify_scope_pick_required,
|
|
106
90
|
_suggest_latest_final_report,
|
|
107
91
|
)
|
|
@@ -198,38 +182,7 @@ from .steps_options import (
|
|
|
198
182
|
_submit_related_tasks,
|
|
199
183
|
_submit_related_tasks_pick,
|
|
200
184
|
)
|
|
201
|
-
from .steps_roles import
|
|
202
|
-
_build_antigravity_model,
|
|
203
|
-
_build_claude_model,
|
|
204
|
-
_build_codex_model,
|
|
205
|
-
_build_critic_pick,
|
|
206
|
-
_build_critic_text,
|
|
207
|
-
_build_defaults_or_custom,
|
|
208
|
-
_build_executor,
|
|
209
|
-
_build_executor_model,
|
|
210
|
-
_build_grok_model,
|
|
211
|
-
_build_kimi_model,
|
|
212
|
-
_build_lead_model,
|
|
213
|
-
_build_report_writer_model,
|
|
214
|
-
_build_reuse_previous,
|
|
215
|
-
_build_workers_custom,
|
|
216
|
-
_build_workers_override,
|
|
217
|
-
_submit_antigravity_model,
|
|
218
|
-
_submit_claude_model,
|
|
219
|
-
_submit_codex_model,
|
|
220
|
-
_submit_critic_pick,
|
|
221
|
-
_submit_critic_text,
|
|
222
|
-
_submit_defaults_or_custom,
|
|
223
|
-
_submit_executor,
|
|
224
|
-
_submit_executor_model,
|
|
225
|
-
_submit_grok_model,
|
|
226
|
-
_submit_kimi_model,
|
|
227
|
-
_submit_lead_model,
|
|
228
|
-
_submit_report_writer_model,
|
|
229
|
-
_submit_reuse_previous,
|
|
230
|
-
_submit_workers_custom,
|
|
231
|
-
_submit_workers_override,
|
|
232
|
-
)
|
|
185
|
+
from .steps_roles import _build_reuse_previous, _submit_reuse_previous
|
|
233
186
|
from .confirmation import _build_confirm, _submit_confirm
|
|
234
187
|
|
|
235
188
|
|
|
@@ -298,8 +251,7 @@ STEPS: list[Step] = [
|
|
|
298
251
|
applies=lambda s: s.is_new_task is None,
|
|
299
252
|
build=_build_task_pick, submit=_submit_task_pick,
|
|
300
253
|
owns=("is_new_task", "task_group", "task_id", "task_type",
|
|
301
|
-
"existing_brief_path",
|
|
302
|
-
"profile_optional_workers",
|
|
254
|
+
"existing_brief_path",
|
|
303
255
|
"task_group_suggestion", "task_id_suggestion",
|
|
304
256
|
"task_group_pending_text", "task_id_pending_text")),
|
|
305
257
|
Step(S_TASK_GROUP,
|
|
@@ -355,15 +307,13 @@ STEPS: list[Step] = [
|
|
|
355
307
|
or (bool(s.task_group) and bool(s.brief_path)))
|
|
356
308
|
and S_TASK_TYPE not in s.answered),
|
|
357
309
|
build=_build_task_type, submit=_submit_task_type,
|
|
358
|
-
owns=("task_type", "
|
|
359
|
-
"reuse_worktree")),
|
|
310
|
+
owns=("task_type", "reuse_worktree")),
|
|
360
311
|
Step(S_TASK_TYPE_TEXT,
|
|
361
312
|
applies=lambda s: (S_TASK_TYPE in s.answered
|
|
362
313
|
and not s.task_type
|
|
363
314
|
and S_TASK_TYPE_TEXT not in s.answered),
|
|
364
315
|
build=_build_task_type_text, submit=_submit_task_type_text,
|
|
365
|
-
owns=("task_type", "
|
|
366
|
-
"reuse_worktree")),
|
|
316
|
+
owns=("task_type", "reuse_worktree")),
|
|
367
317
|
# downstream task-type 인데 carry-in 할 brief 가 없을 때의 fallback.
|
|
368
318
|
# release-handoff 는 brief 자체가 없는 phase 라 대상에서 빠진다.
|
|
369
319
|
Step(S_BRIEF_CARRY,
|
|
@@ -393,7 +343,6 @@ STEPS: list[Step] = [
|
|
|
393
343
|
owns=("task_id", "task_id_pending_text")),
|
|
394
344
|
Step(S_REUSE_PREVIOUS,
|
|
395
345
|
applies=lambda s: (_identity_ready(s)
|
|
396
|
-
and s.use_defaults is None
|
|
397
346
|
and s.reuse_previous is None
|
|
398
347
|
and s.task_type in _RESUME_REUSE_PHASES
|
|
399
348
|
and _has_prior_run_inputs(s)),
|
|
@@ -522,126 +471,10 @@ STEPS: list[Step] = [
|
|
|
522
471
|
and S_HANDOFF_STAGE_PICK not in s.answered),
|
|
523
472
|
build=_build_handoff_stage_pick, submit=_submit_handoff_stage_pick,
|
|
524
473
|
owns=("handoff_stages", "approved_plan_path")),
|
|
525
|
-
|
|
526
|
-
applies=lambda s: (s.task_type == "implementation"
|
|
527
|
-
and not _role_selection_enabled(s)
|
|
528
|
-
and bool(s.approved_plan_path)
|
|
529
|
-
and not s.executor),
|
|
530
|
-
build=_build_executor, submit=_submit_executor,
|
|
531
|
-
owns=("executor",)),
|
|
532
|
-
Step(S_CRITIC_PICK,
|
|
533
|
-
applies=lambda s: (not _role_selection_enabled(s)
|
|
534
|
-
and s.task_type in ("requirements-discovery", "error-analysis", "implementation-planning", "final-verification")
|
|
535
|
-
and not s.critic
|
|
536
|
-
and not s.critic_pending_text
|
|
537
|
-
and S_CRITIC_PICK not in s.answered),
|
|
538
|
-
build=_build_critic_pick, submit=_submit_critic_pick,
|
|
539
|
-
owns=("critic", "critic_pending_text")),
|
|
540
|
-
Step(S_CRITIC_TEXT,
|
|
541
|
-
applies=lambda s: (not _role_selection_enabled(s)
|
|
542
|
-
and s.critic_pending_text
|
|
543
|
-
and S_CRITIC_TEXT not in s.answered),
|
|
544
|
-
build=_build_critic_text, submit=_submit_critic_text,
|
|
545
|
-
owns=("critic", "critic_pending_text")),
|
|
546
|
-
Step(S_DEFAULTS_OR_CUSTOM,
|
|
547
|
-
applies=lambda s: (_identity_ready(s)
|
|
548
|
-
and not _role_selection_enabled(s)
|
|
549
|
-
and s.use_defaults is None),
|
|
550
|
-
build=_build_defaults_or_custom, submit=_submit_defaults_or_custom,
|
|
551
|
-
owns=("use_defaults",)),
|
|
552
|
-
# Worker roster is ALWAYS prompted (independent of the defaults /
|
|
553
|
-
# customize branch) when the profile has analyser-candidate workers
|
|
554
|
-
# (required ∪ optional, minus report-writer). Users repeatedly hit
|
|
555
|
-
# the failure mode where the lead silently picked "Use defaults"
|
|
556
|
-
# and the worker prompt never appeared — defaults should govern
|
|
557
|
-
# model choice, not worker selection. `implementation` task-type is
|
|
558
|
-
# still skipped because it runs lead + executor only.
|
|
559
|
-
Step(S_WORKERS_OVERRIDE,
|
|
560
|
-
applies=lambda s: (s.task_type != "implementation"
|
|
561
|
-
and not _role_selection_enabled(s)
|
|
562
|
-
and any(
|
|
563
|
-
w != "report-writer"
|
|
564
|
-
for w in (s.profile_workers
|
|
565
|
-
+ s.profile_optional_workers)
|
|
566
|
-
)
|
|
567
|
-
and S_WORKERS_OVERRIDE not in s.answered),
|
|
568
|
-
build=_build_workers_override, submit=_submit_workers_override,
|
|
569
|
-
owns=("workers_override", "workers_custom_pending")),
|
|
570
|
-
# "직접 선택" 을 고른 run 에서만 등장한다 — 기본 로스터에서 워커를 빼는
|
|
571
|
-
# 축소가 가능한 유일한 화면.
|
|
572
|
-
Step(S_WORKERS_CUSTOM,
|
|
573
|
-
applies=lambda s: (not _role_selection_enabled(s)
|
|
574
|
-
and s.workers_custom_pending
|
|
575
|
-
and S_WORKERS_CUSTOM not in s.answered),
|
|
576
|
-
build=_build_workers_custom, submit=_submit_workers_custom,
|
|
577
|
-
owns=("workers_override", "workers_custom_pending")),
|
|
578
|
-
Step(S_LEAD_MODEL,
|
|
579
|
-
applies=lambda s: (s.use_defaults is False
|
|
580
|
-
and not _role_selection_enabled(s)
|
|
581
|
-
and S_LEAD_MODEL not in s.answered),
|
|
582
|
-
build=_build_lead_model, submit=_submit_lead_model,
|
|
583
|
-
owns=("lead_provider", "lead_model")),
|
|
584
|
-
Step(S_EXECUTOR_MODEL,
|
|
585
|
-
applies=lambda s: (s.use_defaults is False
|
|
586
|
-
and not _role_selection_enabled(s)
|
|
587
|
-
and s.task_type == "implementation"
|
|
588
|
-
and S_EXECUTOR_MODEL not in s.answered),
|
|
589
|
-
build=_build_executor_model, submit=_submit_executor_model,
|
|
590
|
-
owns=("claude_model", "codex_model", "antigravity_model")),
|
|
591
|
-
Step(S_CLAUDE_MODEL,
|
|
592
|
-
applies=lambda s: (s.use_defaults is False
|
|
593
|
-
and not _role_selection_enabled(s)
|
|
594
|
-
and s.task_type != "implementation"
|
|
595
|
-
and "claude" in _resolved_roster(s)
|
|
596
|
-
and S_CLAUDE_MODEL not in s.answered),
|
|
597
|
-
build=_build_claude_model, submit=_submit_claude_model,
|
|
598
|
-
owns=("claude_model",)),
|
|
599
|
-
Step(S_CODEX_MODEL,
|
|
600
|
-
applies=lambda s: (s.use_defaults is False
|
|
601
|
-
and not _role_selection_enabled(s)
|
|
602
|
-
and s.task_type != "implementation"
|
|
603
|
-
and "codex" in _resolved_roster(s)
|
|
604
|
-
and S_CODEX_MODEL not in s.answered),
|
|
605
|
-
build=_build_codex_model, submit=_submit_codex_model,
|
|
606
|
-
owns=("codex_model",)),
|
|
607
|
-
Step(S_ANTIGRAVITY_MODEL,
|
|
608
|
-
applies=lambda s: (s.use_defaults is False
|
|
609
|
-
and not _role_selection_enabled(s)
|
|
610
|
-
and s.task_type != "implementation"
|
|
611
|
-
and "antigravity" in _resolved_roster(s)
|
|
612
|
-
and S_ANTIGRAVITY_MODEL not in s.answered),
|
|
613
|
-
build=_build_antigravity_model, submit=_submit_antigravity_model,
|
|
614
|
-
owns=("antigravity_model",)),
|
|
615
|
-
Step(S_GROK_MODEL,
|
|
616
|
-
applies=lambda s: (s.use_defaults is False
|
|
617
|
-
and not _role_selection_enabled(s)
|
|
618
|
-
and s.task_type != "implementation"
|
|
619
|
-
and "grok" in _resolved_roster(s)
|
|
620
|
-
and S_GROK_MODEL not in s.answered),
|
|
621
|
-
build=_build_grok_model, submit=_submit_grok_model,
|
|
622
|
-
owns=("grok_model",)),
|
|
623
|
-
Step(S_KIMI_MODEL,
|
|
624
|
-
applies=lambda s: (s.use_defaults is False
|
|
625
|
-
and not _role_selection_enabled(s)
|
|
626
|
-
and s.task_type != "implementation"
|
|
627
|
-
and "kimi" in _resolved_roster(s)
|
|
628
|
-
and S_KIMI_MODEL not in s.answered),
|
|
629
|
-
build=_build_kimi_model, submit=_submit_kimi_model,
|
|
630
|
-
owns=("kimi_model",)),
|
|
631
|
-
Step(S_REPORT_WRITER_MODEL,
|
|
632
|
-
applies=lambda s: (s.use_defaults is False
|
|
633
|
-
and not _role_selection_enabled(s)
|
|
634
|
-
and (s.task_type == "implementation"
|
|
635
|
-
or "report-writer" in _resolved_roster(s))
|
|
636
|
-
and S_REPORT_WRITER_MODEL not in s.answered),
|
|
637
|
-
build=_build_report_writer_model, submit=_submit_report_writer_model,
|
|
638
|
-
owns=("report_writer_provider", "report_writer_model")),
|
|
639
|
-
# directive(이번 run 의 추가 지시)는 기본값/커스터마이즈와 무관하게 항상
|
|
640
|
-
# 묻는다 — 매 run 마다 줄 수 있는 입력이므로 'Use defaults' 분기 뒤에 숨기지
|
|
641
|
-
# 않는다. (use_defaults is not None: defaults_or_custom 답 이후에만 등장)
|
|
474
|
+
# 역할 화면은 이 자리(directive 앞)에서 끼어든다 — `_ROLE_SELECTION_BOUNDARY`.
|
|
642
475
|
Step(S_DIRECTIVE_PICK,
|
|
643
476
|
applies=lambda s: (S_DIRECTIVE_PICK not in s.answered
|
|
644
|
-
and s
|
|
477
|
+
and _identity_ready(s)),
|
|
645
478
|
build=_build_directive_pick, submit=_submit_directive_pick,
|
|
646
479
|
owns=("directive", "directive_pending_text")),
|
|
647
480
|
Step(S_DIRECTIVE,
|
|
@@ -650,20 +483,16 @@ STEPS: list[Step] = [
|
|
|
650
483
|
build=_build_directive, submit=_submit_directive,
|
|
651
484
|
owns=("directive", "directive_pending_text")),
|
|
652
485
|
Step(S_RELATED_TASKS_PICK,
|
|
653
|
-
applies=lambda s: (s
|
|
486
|
+
applies=lambda s: (_identity_ready(s)
|
|
654
487
|
and S_RELATED_TASKS_PICK not in s.answered),
|
|
655
488
|
build=_build_related_tasks_pick, submit=_submit_related_tasks_pick,
|
|
656
489
|
owns=("related_tasks_raw", "related_tasks_pending_text")),
|
|
657
490
|
Step(S_RELATED_TASKS,
|
|
658
|
-
applies=lambda s: (s
|
|
491
|
+
applies=lambda s: (_identity_ready(s)
|
|
659
492
|
and s.related_tasks_pending_text
|
|
660
493
|
and S_RELATED_TASKS not in s.answered),
|
|
661
494
|
build=_build_related_tasks, submit=_submit_related_tasks,
|
|
662
495
|
owns=("related_tasks_raw", "related_tasks_pending_text")),
|
|
663
|
-
# clarification(직전 phase final-report 입력)은 customize 전용이 아니다.
|
|
664
|
-
# 이어가기에 필수인 직전 final-report 가 존재하면 use_defaults 와 무관하게
|
|
665
|
-
# 입력 기회를 노출한다. (과거: use_defaults 게이트 뒤에 숨어 "Use defaults"
|
|
666
|
-
# 를 고르면 직전 리포트가 통째로 누락됐다.)
|
|
667
496
|
# implementation-planning 의 `--clarification-response` 는 같은 phase 의
|
|
668
497
|
# 계획서만 받는다(`run._validate_planning_entry_inputs`). 그 계획서가 없는
|
|
669
498
|
# 첫 계획 run 에서 이 화면은 `없음 / 직접 입력` 뿐이고, 직접 입력으로 넣을
|
|
@@ -673,11 +502,9 @@ STEPS: list[Step] = [
|
|
|
673
502
|
# 함께 전달하므로, 계획 run 은 이어받을 계획서가 있을 때만 여기를 묻는다.
|
|
674
503
|
Step(S_CLARIFICATION_PICK,
|
|
675
504
|
applies=lambda s: (S_CLARIFICATION_PICK not in s.answered
|
|
676
|
-
and s
|
|
677
|
-
and (
|
|
678
|
-
|
|
679
|
-
else (s.use_defaults is False
|
|
680
|
-
or bool(_suggest_latest_final_report(s))))),
|
|
505
|
+
and _identity_ready(s)
|
|
506
|
+
and (s.task_type != "implementation-planning"
|
|
507
|
+
or bool(_suggest_latest_final_report(s)))),
|
|
681
508
|
build=_build_clarification_pick, submit=_submit_clarification_pick,
|
|
682
509
|
owns=("clarification_response_path", "clarification_pending_text")),
|
|
683
510
|
Step(S_CLARIFICATION,
|
|
@@ -711,20 +538,20 @@ STEPS: list[Step] = [
|
|
|
711
538
|
submit=_submit_selected_direction_pick,
|
|
712
539
|
owns=("selected_direction_path",)),
|
|
713
540
|
Step(S_PR_TEMPLATE_PICK,
|
|
714
|
-
applies=lambda s: (s
|
|
541
|
+
applies=lambda s: (_identity_ready(s)
|
|
715
542
|
and s.task_type == "release-handoff"
|
|
716
543
|
and S_PR_TEMPLATE_PICK not in s.answered),
|
|
717
544
|
build=_build_pr_template_pick, submit=_submit_pr_template_pick,
|
|
718
545
|
owns=("pr_template_path", "pr_template_scope", "pr_template_pending_text")),
|
|
719
546
|
Step(S_PR_TEMPLATE,
|
|
720
|
-
applies=lambda s: (s
|
|
547
|
+
applies=lambda s: (_identity_ready(s)
|
|
721
548
|
and s.task_type == "release-handoff"
|
|
722
549
|
and s.pr_template_pending_text
|
|
723
550
|
and S_PR_TEMPLATE not in s.answered),
|
|
724
551
|
build=_build_pr_template, submit=_submit_pr_template,
|
|
725
552
|
owns=("pr_template_path", "pr_template_scope", "pr_template_pending_text")),
|
|
726
553
|
Step(S_PR_TEMPLATE_SCOPE,
|
|
727
|
-
applies=lambda s: (s
|
|
554
|
+
applies=lambda s: (_identity_ready(s)
|
|
728
555
|
and s.task_type == "release-handoff"
|
|
729
556
|
and bool(s.pr_template_path)
|
|
730
557
|
and S_PR_TEMPLATE_SCOPE not in s.answered),
|
|
@@ -771,48 +598,30 @@ STEPS: list[Step] = [
|
|
|
771
598
|
STEP_BY_ID = {s.id: s for s in STEPS}
|
|
772
599
|
|
|
773
600
|
|
|
601
|
+
# 역할 화면이 끼어드는 STEPS 위치. 이 앞의 단계로 되감으면 역할 선택도 비운다.
|
|
602
|
+
_ROLE_SELECTION_BOUNDARY = S_DIRECTIVE_PICK
|
|
603
|
+
|
|
604
|
+
|
|
774
605
|
def _ready_for_confirm(s: WizardState) -> bool:
|
|
775
|
-
if
|
|
776
|
-
try:
|
|
777
|
-
if next_role_prompt(s) is not None:
|
|
778
|
-
return False
|
|
779
|
-
except WizardError:
|
|
780
|
-
return False
|
|
781
|
-
followup_ids = [
|
|
782
|
-
S_DIRECTIVE_PICK,
|
|
783
|
-
S_DIRECTIVE,
|
|
784
|
-
S_RELATED_TASKS_PICK,
|
|
785
|
-
S_RELATED_TASKS,
|
|
786
|
-
S_CLARIFICATION_PICK,
|
|
787
|
-
S_CLARIFICATION,
|
|
788
|
-
S_PR_TEMPLATE_PICK,
|
|
789
|
-
S_PR_TEMPLATE,
|
|
790
|
-
S_PR_TEMPLATE_SCOPE,
|
|
791
|
-
]
|
|
792
|
-
return not any(STEP_BY_ID[step_id].applies(s) for step_id in followup_ids)
|
|
793
|
-
if s.use_defaults is None:
|
|
794
|
-
return False
|
|
795
|
-
# Worker roster is required regardless of the defaults / customize
|
|
796
|
-
# branch. Skipping this check let the lead drop into confirm with no
|
|
797
|
-
# worker prompt ever shown — the very failure mode this gate exists
|
|
798
|
-
# to prevent.
|
|
799
|
-
workers_step = STEP_BY_ID[S_WORKERS_OVERRIDE]
|
|
800
|
-
if workers_step.applies(s):
|
|
606
|
+
if not _identity_ready(s):
|
|
801
607
|
return False
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if s.use_defaults:
|
|
805
|
-
return True
|
|
806
|
-
# customize: every customize-branch step must be answered or not-applicable.
|
|
807
|
-
custom_ids = [S_LEAD_MODEL, S_EXECUTOR_MODEL,
|
|
808
|
-
S_CLAUDE_MODEL, S_CODEX_MODEL, S_ANTIGRAVITY_MODEL,
|
|
809
|
-
S_REPORT_WRITER_MODEL, S_DIRECTIVE, S_RELATED_TASKS,
|
|
810
|
-
S_CLARIFICATION, S_PR_TEMPLATE, S_PR_TEMPLATE_SCOPE]
|
|
811
|
-
for sid in custom_ids:
|
|
812
|
-
step = STEP_BY_ID[sid]
|
|
813
|
-
if step.applies(s):
|
|
608
|
+
try:
|
|
609
|
+
if next_role_prompt(s) is not None:
|
|
814
610
|
return False
|
|
815
|
-
|
|
611
|
+
except WizardError:
|
|
612
|
+
return False
|
|
613
|
+
followup_ids = [
|
|
614
|
+
S_DIRECTIVE_PICK,
|
|
615
|
+
S_DIRECTIVE,
|
|
616
|
+
S_RELATED_TASKS_PICK,
|
|
617
|
+
S_RELATED_TASKS,
|
|
618
|
+
S_CLARIFICATION_PICK,
|
|
619
|
+
S_CLARIFICATION,
|
|
620
|
+
S_PR_TEMPLATE_PICK,
|
|
621
|
+
S_PR_TEMPLATE,
|
|
622
|
+
S_PR_TEMPLATE_SCOPE,
|
|
623
|
+
]
|
|
624
|
+
return not any(STEP_BY_ID[step_id].applies(s) for step_id in followup_ids)
|
|
816
625
|
|
|
817
626
|
|
|
818
627
|
def _reset_from(state: WizardState, target_step: str) -> None:
|
|
@@ -823,7 +632,7 @@ def _reset_from(state: WizardState, target_step: str) -> None:
|
|
|
823
632
|
return
|
|
824
633
|
role_boundary = next(
|
|
825
634
|
i for i, step in enumerate(STEPS)
|
|
826
|
-
if step.id ==
|
|
635
|
+
if step.id == _ROLE_SELECTION_BOUNDARY
|
|
827
636
|
)
|
|
828
637
|
if idx < role_boundary:
|
|
829
638
|
_clear_role_selection(state)
|
|
@@ -854,7 +663,6 @@ def _reset_from(state: WizardState, target_step: str) -> None:
|
|
|
854
663
|
S_FEATURE_EVIDENCE: "feature_evidence_pending_text",
|
|
855
664
|
S_PROJECT_EVIDENCE: "project_evidence_pending_text",
|
|
856
665
|
S_ANALYSIS_TARGET: "analysis_target_pending_text",
|
|
857
|
-
S_WORKERS_CUSTOM: "workers_custom_pending",
|
|
858
666
|
}
|
|
859
667
|
pending_field = direct_input_pending.get(target_step)
|
|
860
668
|
if pending_field is not None:
|
|
@@ -15,7 +15,7 @@ from .state import (
|
|
|
15
15
|
WizardError,
|
|
16
16
|
WizardState,
|
|
17
17
|
_load_role_profile_for_state,
|
|
18
|
-
|
|
18
|
+
_role_selection_ready,
|
|
19
19
|
)
|
|
20
20
|
from .roles import _host_session_context, _selectable_static_requirements
|
|
21
21
|
|
|
@@ -40,7 +40,7 @@ def _stage_intent(state: WizardState) -> WizardStageIntent:
|
|
|
40
40
|
def render_role_args(state: WizardState) -> list[str]:
|
|
41
41
|
"""Return ordered canonical role count/model CLI tokens."""
|
|
42
42
|
role_argv: list[str] = []
|
|
43
|
-
if
|
|
43
|
+
if _role_selection_ready(state):
|
|
44
44
|
profile = _load_role_profile_for_state(state)
|
|
45
45
|
requirements = _selectable_static_requirements(state, profile)
|
|
46
46
|
for requirement in profile.roles:
|
|
@@ -52,7 +52,6 @@ from .state import (
|
|
|
52
52
|
_identity_ready,
|
|
53
53
|
_is_role_selection_step,
|
|
54
54
|
_load_role_profile_for_state,
|
|
55
|
-
_role_selection_enabled,
|
|
56
55
|
)
|
|
57
56
|
from .prompts import _opt, _p
|
|
58
57
|
|
|
@@ -531,9 +530,8 @@ def _load_context(state: WizardState) -> tuple[AssignmentContext, ModelDefaultSc
|
|
|
531
530
|
|
|
532
531
|
def next_role_prompt(state: WizardState) -> Prompt | None:
|
|
533
532
|
"""프로필 순서대로 역할마다 화면 하나 — 답이 없는 첫 역할의 화면을 낸다."""
|
|
534
|
-
if not
|
|
533
|
+
if not _identity_ready(state):
|
|
535
534
|
return None
|
|
536
|
-
state.use_defaults = False
|
|
537
535
|
profile = _load_role_profile_for_state(state)
|
|
538
536
|
requirements = _static_requirements(profile)
|
|
539
537
|
context, scopes = _load_context(state)
|
|
@@ -8,7 +8,7 @@ from collections.abc import Mapping
|
|
|
8
8
|
from typing import Any, Optional
|
|
9
9
|
|
|
10
10
|
from okstra_ctl.ids import slugify_task_segment
|
|
11
|
-
from okstra_ctl.brief_frontmatter import read_brief_frontmatter
|
|
11
|
+
from okstra_ctl.brief_frontmatter import brief_task_id, read_brief_frontmatter
|
|
12
12
|
from okstra_ctl.analysis_inputs import (
|
|
13
13
|
ANALYSIS_TASK_TYPES,
|
|
14
14
|
AnalysisInputError,
|
|
@@ -24,11 +24,6 @@ from okstra_ctl.incremental_scope import (
|
|
|
24
24
|
from okstra_ctl.final_report_paths import final_report_data_path
|
|
25
25
|
from okstra_ctl.plan_run_root import list_implementation_planning_reports
|
|
26
26
|
from okstra_ctl.stage_map import StageMapError, parse_stage_map_file
|
|
27
|
-
from okstra_ctl.workers import (
|
|
28
|
-
normalize_workers,
|
|
29
|
-
resolve_optional_workers,
|
|
30
|
-
resolve_profile_workers,
|
|
31
|
-
)
|
|
32
27
|
from okstra_ctl.workflow import PHASE_SEQUENCE
|
|
33
28
|
from okstra_ctl import worktree_registry
|
|
34
29
|
from okstra_ctl.worktree import is_git_work_tree, main_worktree_path
|
|
@@ -49,7 +44,7 @@ from .ids import (
|
|
|
49
44
|
_BRIEF_HEAD_LINES,
|
|
50
45
|
_BRIEF_NEXT_PHASE_RE,
|
|
51
46
|
)
|
|
52
|
-
from .state import WizardError, WizardState,
|
|
47
|
+
from .state import WizardError, WizardState, _slug_or_die
|
|
53
48
|
from .prompts import _p
|
|
54
49
|
|
|
55
50
|
|
|
@@ -89,18 +84,18 @@ def _brief_suggestions(path: Path) -> tuple[str, str]:
|
|
|
89
84
|
the brief's frontmatter, or empty strings when no usable value exists.
|
|
90
85
|
|
|
91
86
|
- ``task_group`` ← frontmatter ``task-group``.
|
|
92
|
-
- ``task_id`` ← frontmatter ``
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
- ``task_id`` ← frontmatter ``task-id`` (okstra-manager child
|
|
88
|
+
briefs), else ``brief-id`` (the filename stem in
|
|
89
|
+
okstra-brief-gen output).
|
|
95
90
|
|
|
96
91
|
A brief without frontmatter, or with placeholder values, yields two
|
|
97
92
|
empty strings — callers fall back to plain-text input.
|
|
98
93
|
"""
|
|
99
94
|
fm = read_brief_frontmatter(path)
|
|
100
95
|
tg_raw = fm.get("task-group", "")
|
|
101
|
-
|
|
96
|
+
tid_raw = brief_task_id(fm)
|
|
102
97
|
tg = "" if _looks_like_template_placeholder(tg_raw) else tg_raw
|
|
103
|
-
tid = "" if _looks_like_template_placeholder(
|
|
98
|
+
tid = "" if _looks_like_template_placeholder(tid_raw) else tid_raw
|
|
104
99
|
return tg, tid
|
|
105
100
|
|
|
106
101
|
|
|
@@ -145,8 +140,8 @@ def _accept_brief_path(state: WizardState, path: Path) -> None:
|
|
|
145
140
|
|
|
146
141
|
New-task runs now ask for ``task_group`` before the brief so the wizard
|
|
147
142
|
can offer same-group brief candidates. If the selected brief carries a
|
|
148
|
-
conflicting frontmatter ``task-group``, fail fast; otherwise
|
|
149
|
-
|
|
143
|
+
conflicting frontmatter ``task-group``, fail fast; otherwise use the
|
|
144
|
+
brief's task-id (``_brief_suggestions``) as the task-id suggestion.
|
|
150
145
|
"""
|
|
151
146
|
tg_suggestion, tid_suggestion = _brief_suggestions(path)
|
|
152
147
|
if state.task_group and tg_suggestion:
|
|
@@ -188,32 +183,6 @@ def _git_main_worktree(project_root: Path) -> Path:
|
|
|
188
183
|
return main_worktree_path(project_root)
|
|
189
184
|
|
|
190
185
|
|
|
191
|
-
def _load_profile_workers(workspace_root: Path, task_type: str) -> list[str]:
|
|
192
|
-
return resolve_profile_workers(_profile_path(workspace_root, task_type))
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def _load_profile_optional_workers(
|
|
196
|
-
workspace_root: Path, task_type: str
|
|
197
|
-
) -> list[str]:
|
|
198
|
-
return resolve_optional_workers(_profile_path(workspace_root, task_type))
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def _resolved_roster(state: WizardState) -> list[str]:
|
|
202
|
-
"""Effective worker list AFTER override. Implementation: profile default
|
|
203
|
-
(caller never asks for override). Others: override or profile default."""
|
|
204
|
-
if state.task_type == "implementation":
|
|
205
|
-
roster = list(state.profile_workers)
|
|
206
|
-
# implementation 은 roster override 단계를 건너뛰므로, executor 로 명시
|
|
207
|
-
# 선택한 워커(예: antigravity)가 프로필 기본 roster 에 없으면 여기서
|
|
208
|
-
# 합류시켜야 render-bundle 의 executor∈roster 가드를 통과한다.
|
|
209
|
-
if state.executor and state.executor not in roster:
|
|
210
|
-
roster.append(state.executor)
|
|
211
|
-
return roster
|
|
212
|
-
if state.workers_override.strip():
|
|
213
|
-
return normalize_workers(state.workers_override)
|
|
214
|
-
return list(state.profile_workers)
|
|
215
|
-
|
|
216
|
-
|
|
217
186
|
# ---- Worktree resolution ------------------------------------------------
|
|
218
187
|
|
|
219
188
|
def _resolve_reuse_worktree(state: WizardState) -> bool:
|