okstra 0.201.3 → 0.204.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 -3
- package/dist/cli-registry.mjs +7 -7
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/lifecycle/install.mjs +50 -124
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/lifecycle/setup.mjs +15 -0
- package/dist/commands/lifecycle/setup.mjs.map +1 -1
- package/dist/commands/memory/memory.mjs +41 -8
- package/dist/commands/memory/memory.mjs.map +1 -1
- package/dist/lib/citation-guidance.d.mts +21 -0
- package/dist/lib/citation-guidance.mjs +79 -0
- package/dist/lib/citation-guidance.mjs.map +1 -0
- package/dist/lib/install-assets.mjs +3 -0
- package/dist/lib/install-assets.mjs.map +1 -1
- package/dist/lib/runtime-manifest.mjs +2 -1
- package/dist/lib/runtime-manifest.mjs.map +1 -1
- package/dist/lib/types.d.mts +2 -1
- package/docs/architecture/storage-model.md +17 -10
- package/docs/architecture.md +26 -20
- package/docs/cli.md +16 -13
- package/docs/contributor-change-matrix.md +3 -2
- package/docs/performance-improvement-plan-v2.md +2 -3
- package/docs/project-structure-overview.md +38 -9
- package/docs/task-process/README.md +1 -1
- package/docs/task-process/common-flow.md +1 -1
- package/docs/task-process/final-verification.md +3 -1
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +36 -39
- package/package.json +1 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/common.json +28 -0
- package/runtime/agents/operations/code-review.json +6 -0
- package/runtime/agents/operations/report-translation.json +6 -0
- package/runtime/agents/operations/schedule-verification.json +6 -0
- package/runtime/agents/roles/analyser.json +18 -0
- package/runtime/agents/roles/critic.json +18 -0
- package/runtime/agents/roles/designer.json +18 -0
- package/runtime/agents/roles/implementer.json +20 -0
- package/runtime/agents/roles/leader.json +20 -0
- package/runtime/agents/roles/planner.json +18 -0
- package/runtime/agents/roles/report-writer.json +19 -0
- package/runtime/agents/roles/translator.json +19 -0
- package/runtime/agents/roles/verifier.json +18 -0
- package/runtime/bin/lib/okstra/usage.sh +5 -5
- package/runtime/prompts/duties/acceptance-critic.json +32 -0
- package/runtime/prompts/duties/acceptance-verifier.json +32 -0
- package/runtime/prompts/duties/analysis-worker.json +32 -0
- package/runtime/prompts/duties/code-reviewer.json +32 -0
- package/runtime/prompts/duties/diagnosis-worker.json +32 -0
- package/runtime/prompts/duties/direction-selection-worker.json +32 -0
- package/runtime/prompts/duties/discovery-worker.json +32 -0
- package/runtime/prompts/duties/implementation-executor.json +32 -0
- package/runtime/prompts/duties/implementation-verifier.json +32 -0
- package/runtime/prompts/duties/lead.json +32 -0
- package/runtime/prompts/duties/planning-worker.json +36 -0
- package/runtime/prompts/duties/report-writer.json +32 -0
- package/runtime/prompts/duties/reverification-worker.json +32 -0
- package/runtime/prompts/duties/schedule-verifier.json +32 -0
- package/runtime/prompts/duties/scope-critic.json +32 -0
- package/runtime/prompts/duties/technical-verification-worker.json +32 -0
- package/runtime/prompts/duties/translator.json +32 -0
- package/runtime/prompts/launch.template.md +3 -2
- package/runtime/prompts/lead/adapters/cmux.md +1 -1
- package/runtime/prompts/lead/convergence.md +4 -4
- package/runtime/prompts/lead/okstra-lead-contract.md +115 -6
- package/runtime/prompts/lead/plan-body-verification.md +6 -6
- package/runtime/prompts/lead/report-writer.md +3 -3
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +4 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/change-impact-analysis.json +31 -0
- package/runtime/prompts/profiles/change-impact-analysis.md +0 -20
- package/runtime/prompts/profiles/error-analysis.json +39 -0
- package/runtime/prompts/profiles/error-analysis.md +0 -25
- package/runtime/prompts/profiles/feature-analysis.json +31 -0
- package/runtime/prompts/profiles/feature-analysis.md +0 -20
- package/runtime/prompts/profiles/final-verification.json +30 -0
- package/runtime/prompts/profiles/final-verification.md +3 -22
- package/runtime/prompts/profiles/forbidden-actions.json +4 -3
- package/runtime/prompts/profiles/implementation-option-selection.json +31 -0
- package/runtime/prompts/profiles/implementation-option-selection.md +0 -20
- package/runtime/prompts/profiles/implementation-planning.json +40 -0
- package/runtime/prompts/profiles/implementation-planning.md +6 -29
- package/runtime/prompts/profiles/implementation.json +30 -0
- package/runtime/prompts/profiles/implementation.md +1 -20
- package/runtime/prompts/profiles/improvement-discovery.json +31 -0
- package/runtime/prompts/profiles/improvement-discovery.md +0 -20
- package/runtime/prompts/profiles/project-analysis.json +31 -0
- package/runtime/prompts/profiles/project-analysis.md +0 -20
- package/runtime/prompts/profiles/release-handoff.json +5 -0
- package/runtime/prompts/profiles/release-handoff.md +71 -73
- package/runtime/prompts/profiles/requirements-discovery.json +39 -0
- package/runtime/prompts/profiles/requirements-discovery.md +0 -25
- package/runtime/prompts/profiles/technical-verification.json +39 -0
- package/runtime/prompts/profiles/technical-verification.md +0 -25
- package/runtime/prompts/wizard/prompts.ko.json +12 -17
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/relay.md +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +3 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/manifest.json +1 -1
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -3
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/worker-session.md +108 -0
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +2 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +2 -0
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +8 -1
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +8 -0
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +23 -6
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +6 -2
- package/runtime/python/okstra_ctl/agent/invocation.py +168 -113
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +120 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +107 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +0 -49
- package/runtime/python/okstra_ctl/analysis_packet.py +4 -1
- package/runtime/python/okstra_ctl/application/open_worker.py +6 -1
- package/runtime/python/okstra_ctl/assignment_resolver.py +16 -5
- package/runtime/python/okstra_ctl/cmux.py +69 -20
- package/runtime/python/okstra_ctl/code_review_target.py +16 -8
- package/runtime/python/okstra_ctl/conformance.py +43 -0
- package/runtime/python/okstra_ctl/consumers.py +6 -3
- package/runtime/python/okstra_ctl/container.py +31 -8
- package/runtime/python/okstra_ctl/context_cost.py +11 -15
- package/runtime/python/okstra_ctl/contract_refreeze.py +156 -0
- package/runtime/python/okstra_ctl/convergence_critic_prompt.py +4 -6
- package/runtime/python/okstra_ctl/convergence_provenance.py +81 -18
- package/runtime/python/okstra_ctl/design_prep.py +34 -1
- package/runtime/python/okstra_ctl/dispatch_core.py +53 -27
- package/runtime/python/okstra_ctl/domain/host.py +5 -0
- package/runtime/python/okstra_ctl/domain/worker_runtime.py +10 -0
- package/runtime/python/okstra_ctl/error_report.py +4 -3
- package/runtime/python/okstra_ctl/execution_manifest.py +71 -18
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +21 -21
- package/runtime/python/okstra_ctl/handoff.py +167 -277
- package/runtime/python/okstra_ctl/implementation_stage.py +9 -0
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +113 -0
- package/runtime/python/okstra_ctl/lead_progress.py +1 -1
- package/runtime/python/okstra_ctl/legacy_model_selection.py +2 -2
- package/runtime/python/okstra_ctl/manager_cli.py +175 -14
- package/runtime/python/okstra_ctl/manager_launch.py +41 -19
- package/runtime/python/okstra_ctl/manager_paths.py +22 -3
- package/runtime/python/okstra_ctl/manager_split.py +474 -0
- package/runtime/python/okstra_ctl/manager_store.py +331 -21
- package/runtime/python/okstra_ctl/manager_sync.py +37 -16
- package/runtime/python/okstra_ctl/manager_view.py +217 -0
- package/runtime/python/okstra_ctl/model_discovery.py +30 -0
- package/runtime/python/okstra_ctl/model_io/lines.py +14 -1
- package/runtime/python/okstra_ctl/model_io/renderers.py +4 -3
- package/runtime/python/okstra_ctl/models.py +1 -1
- package/runtime/python/okstra_ctl/next_phase.py +16 -6
- package/runtime/python/okstra_ctl/operation_invocation.py +86 -0
- package/runtime/python/okstra_ctl/option_comparison.py +168 -0
- package/runtime/python/okstra_ctl/path_hints.py +9 -0
- package/runtime/python/okstra_ctl/paths.py +3 -0
- package/runtime/python/okstra_ctl/plan_items_cli.py +6 -1
- package/runtime/python/okstra_ctl/profile_show.py +42 -1
- package/runtime/python/okstra_ctl/qa_commands.py +15 -0
- package/runtime/python/okstra_ctl/registry/host_discovery.py +20 -12
- package/runtime/python/okstra_ctl/registry/host_registry.py +11 -0
- package/runtime/python/okstra_ctl/render.py +50 -0
- package/runtime/python/okstra_ctl/report_contract.py +1 -1
- package/runtime/python/okstra_ctl/report_finalize.py +13 -6
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -21
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +21 -3
- package/runtime/python/okstra_ctl/report_html/visualizations.py +0 -5
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +177 -17
- package/runtime/python/okstra_ctl/report_translation.py +2 -1
- package/runtime/python/okstra_ctl/report_translation_dispatch.py +69 -9
- package/runtime/python/okstra_ctl/role_requirements.py +142 -129
- package/runtime/python/okstra_ctl/rollup.py +3 -1
- package/runtime/python/okstra_ctl/run.py +76 -29
- package/runtime/python/okstra_ctl/schedule_semantics.py +17 -6
- package/runtime/python/okstra_ctl/stage_fix_carry.py +23 -4
- package/runtime/python/okstra_ctl/stage_integrate.py +178 -18
- package/runtime/python/okstra_ctl/stage_map.py +16 -2
- package/runtime/python/okstra_ctl/stage_targets.py +209 -43
- package/runtime/python/okstra_ctl/team.py +22 -13
- package/runtime/python/okstra_ctl/time_report.py +2 -1
- package/runtime/python/okstra_ctl/usage_report.py +3 -1
- package/runtime/python/okstra_ctl/verification_target.py +13 -2
- package/runtime/python/okstra_ctl/wizard/confirmation.py +3 -9
- package/runtime/python/okstra_ctl/wizard/ids.py +1 -1
- package/runtime/python/okstra_ctl/wizard/registry.py +1 -1
- package/runtime/python/okstra_ctl/wizard/state.py +3 -5
- package/runtime/python/okstra_ctl/wizard/steps_plan.py +3 -23
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +5 -1
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +35 -7
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +66 -48
- package/runtime/python/okstra_ctl/workflow.py +1 -1
- package/runtime/python/okstra_ctl/worktree/__init__.py +3 -1
- package/runtime/python/okstra_ctl/worktree/naming.py +9 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +38 -9
- package/runtime/python/okstra_token_usage/pricing.py +6 -4
- package/runtime/schemas/agent-common-v1.schema.json +34 -0
- package/runtime/schemas/agent-duty-v1.schema.json +38 -0
- package/runtime/schemas/agent-operation-v1.schema.json +11 -0
- package/runtime/schemas/agent-profile-v1.schema.json +46 -0
- package/runtime/schemas/agent-role-v1.schema.json +29 -0
- package/runtime/schemas/final-report-v2.0.schema.json +118 -97
- package/runtime/schemas/final-report-v3.0.schema.json +118 -97
- package/runtime/skills/okstra-brief-gen/SKILL.md +84 -4
- package/runtime/skills/okstra-chat/SKILL.md +2 -2
- package/runtime/skills/okstra-code-review/SKILL.md +23 -9
- package/runtime/skills/okstra-container-build/SKILL.md +10 -10
- package/runtime/skills/okstra-inspect/SKILL.md +1 -1
- package/runtime/skills/okstra-inspect/facets/cost.md +1 -1
- package/runtime/skills/okstra-inspect/facets/error-zip.md +9 -9
- package/runtime/skills/okstra-inspect/facets/errors.md +16 -16
- package/runtime/skills/okstra-inspect/facets/logs.md +7 -7
- package/runtime/skills/okstra-inspect/facets/recap.md +2 -2
- package/runtime/skills/okstra-inspect/facets/report.md +1 -1
- package/runtime/skills/okstra-inspect/facets/status.md +4 -3
- package/runtime/skills/okstra-inspect/facets/time.md +11 -10
- package/runtime/skills/okstra-manager/SKILL.md +70 -5
- package/runtime/skills/okstra-pr-gen/SKILL.md +6 -5
- package/runtime/skills/okstra-rollup/SKILL.md +5 -5
- package/runtime/skills/okstra-run/SKILL.md +32 -13
- package/runtime/skills/okstra-schedule-gen/SKILL.md +19 -14
- package/runtime/skills/okstra-setup/SKILL.md +21 -10
- package/runtime/skills/okstra-setup/references/project-config.md +7 -6
- package/runtime/skills/okstra-usage/SKILL.md +1 -1
- package/runtime/skills/okstra-user-response/SKILL.md +1 -1
- package/runtime/templates/manager/view.template.html +109 -0
- package/runtime/templates/report-writer-prompt-preamble.md +8 -0
- package/runtime/templates/reports/brief.template.md +14 -4
- package/runtime/templates/reports/html/i18n/en.json +7 -4
- package/runtime/templates/reports/html/i18n/ko.json +7 -4
- package/runtime/templates/reports/html/tasks/final-verification.template.html +2 -2
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +8 -5
- package/runtime/templates/reports/i18n/en.json +1 -1
- package/runtime/templates/reports/md/tasks/release-handoff.template.md +1 -1
- package/runtime/templates/reports/release-handoff-input.template.md +6 -4
- package/runtime/templates/translator-prompt-preamble.md +36 -0
- package/runtime/validators/checks/validate-assets-01.py +7 -8
- package/runtime/validators/validate-brief.py +77 -2
- package/runtime/validators/validate-implementation-plan-stages.py +2 -1
- package/runtime/validators/validate-run.py +59 -9
- package/runtime/validators/validate-schedule.py +9 -0
- package/docs/for-ai/README.md +0 -68
- package/docs/for-ai/skills/okstra-brief-gen.md +0 -262
- package/docs/for-ai/skills/okstra-chat.md +0 -34
- package/docs/for-ai/skills/okstra-code-review.md +0 -57
- package/docs/for-ai/skills/okstra-container-build.md +0 -129
- package/docs/for-ai/skills/okstra-inspect.md +0 -262
- package/docs/for-ai/skills/okstra-manager.md +0 -69
- package/docs/for-ai/skills/okstra-memory.md +0 -126
- package/docs/for-ai/skills/okstra-pr-gen.md +0 -49
- package/docs/for-ai/skills/okstra-rollup.md +0 -114
- package/docs/for-ai/skills/okstra-run.md +0 -250
- package/docs/for-ai/skills/okstra-schedule-gen.md +0 -240
- package/docs/for-ai/skills/okstra-setup.md +0 -158
- package/docs/for-ai/skills/okstra-usage.md +0 -29
- package/docs/for-ai/skills/okstra-user-response.md +0 -72
- package/runtime/agents/workers/claude-worker.md +0 -128
- package/runtime/agents/workers/report-writer-worker.md +0 -37
- package/runtime/agents/workers/translator-worker.md +0 -63
- package/runtime/prompts/duties/acceptance-critic.md +0 -44
- package/runtime/prompts/duties/acceptance-verifier.md +0 -44
- package/runtime/prompts/duties/analysis-worker.md +0 -44
- package/runtime/prompts/duties/code-reviewer.md +0 -44
- package/runtime/prompts/duties/common.md +0 -39
- package/runtime/prompts/duties/diagnosis-worker.md +0 -44
- package/runtime/prompts/duties/direction-selection-worker.md +0 -44
- package/runtime/prompts/duties/discovery-worker.md +0 -44
- package/runtime/prompts/duties/implementation-executor.md +0 -44
- package/runtime/prompts/duties/implementation-verifier.md +0 -44
- package/runtime/prompts/duties/lead.md +0 -44
- package/runtime/prompts/duties/planning-worker.md +0 -52
- package/runtime/prompts/duties/report-writer.md +0 -44
- package/runtime/prompts/duties/reverification-worker.md +0 -44
- package/runtime/prompts/duties/schedule-verifier.md +0 -44
- package/runtime/prompts/duties/scope-critic.md +0 -44
- package/runtime/prompts/duties/technical-verification-worker.md +0 -44
- package/runtime/prompts/duties/translator.md +0 -44
- package/runtime/python/okstra_ctl/pane_title.py +0 -154
|
@@ -27,6 +27,7 @@ from .implementation_options import (
|
|
|
27
27
|
)
|
|
28
28
|
from .report_narrative import writer_owned_data, writer_owned_schema
|
|
29
29
|
from .schema_excerpt import build_schema_excerpt
|
|
30
|
+
from .stage_ledger import build_stage_ledger
|
|
30
31
|
from .scope_provenance import brief_end_state_id_sequence
|
|
31
32
|
|
|
32
33
|
from .exact_coverage import COVERAGE_VERDICT_PRECEDENCE
|
|
@@ -78,6 +79,10 @@ class ReportSynthesisPacket:
|
|
|
78
79
|
participating_analysers: tuple[str, ...] = ()
|
|
79
80
|
# 증분 판정. 저작 계약이 이번 run 의 stage 번호를 말할 수 있는 유일한 출처다.
|
|
80
81
|
incremental_decision: dict[str, Any] | None = None
|
|
82
|
+
# 증분이 아닌 재계획이 이어받아야 하는 이전 계획의 stage 번호. 증분 판정과
|
|
83
|
+
# 배타적이다 — 둘이 함께 실리면 같은 stage 에 "그대로 옮겨라" 와 "다시
|
|
84
|
+
# 써라" 가 동시에 도달한다.
|
|
85
|
+
prior_plan_carry: dict[str, Any] | None = None
|
|
81
86
|
# 넘겨받은 완성 리포트 스키마에서 뽑은 값. 작성자는 스키마 가지를 읽지 않으므로
|
|
82
87
|
# 여기 적어 줘야 도달한다(2026-09-02 실측: `Human Summary` 절 누락, `Verdict
|
|
83
88
|
# Token` 에 `analysis-complete` — 둘 다 HTML 렌더에서야 거절됐다).
|
|
@@ -106,20 +111,83 @@ class ReportSynthesisPacket:
|
|
|
106
111
|
판정을 아는 여기뿐이다.
|
|
107
112
|
"""
|
|
108
113
|
decision = self.incremental_decision
|
|
109
|
-
if
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
if decision:
|
|
115
|
+
return self._incremental_carry_instructions(decision)
|
|
116
|
+
if self.prior_plan_carry:
|
|
117
|
+
return self._prior_plan_carry_instructions(self.prior_plan_carry)
|
|
118
|
+
return []
|
|
119
|
+
|
|
120
|
+
def _incremental_carry_instructions(
|
|
121
|
+
self, decision: Mapping[str, Any],
|
|
122
|
+
) -> list[str]:
|
|
123
|
+
carry_stages = [
|
|
124
|
+
stage for stage in decision.get("carryStages", []) if isinstance(stage, int)
|
|
125
|
+
]
|
|
126
|
+
reverify_stages = [
|
|
127
|
+
stage
|
|
128
|
+
for stage in decision.get("reverifyStages", [])
|
|
129
|
+
if isinstance(stage, int)
|
|
130
|
+
]
|
|
131
|
+
carried = ", ".join(str(stage) for stage in carry_stages)
|
|
132
|
+
# 이월 전부·재검증 0건(`--carry-all-reason`)이 정상 판정이다. 번호 목록을
|
|
133
|
+
# 그대로 끼워 넣으면 "Author only stage(s) ." 가 되어, 덧붙이러 온 run 에게
|
|
134
|
+
# stage 를 하나도 쓰지 말라고 지시한다.
|
|
135
|
+
authoring = (
|
|
136
|
+
f"Author only stage(s) {', '.join(str(s) for s in reverify_stages)}."
|
|
137
|
+
if reverify_stages
|
|
138
|
+
else "Re-author none of them; a stage you add takes the next number "
|
|
139
|
+
f"after {max(carry_stages, default=0)}."
|
|
140
|
+
)
|
|
113
141
|
return [
|
|
114
142
|
"This run is an incremental re-plan. Record the decision verbatim as "
|
|
115
143
|
"`implementationPlanning.incrementalDecision` with keys mode, "
|
|
116
144
|
"reverifyStages, carryStages, reason.",
|
|
117
145
|
f"Copy stage rows {carried} from the \"{CARRIED_STAGES_LABEL}\" source "
|
|
118
146
|
f"into `implementationPlanning.stages` unchanged - every field, byte for "
|
|
119
|
-
f"byte.
|
|
147
|
+
f"byte. {authoring} `okstra incremental-carry` "
|
|
120
148
|
f"rejects a carried stage that changed or is missing.",
|
|
121
149
|
]
|
|
122
150
|
|
|
151
|
+
def _prior_plan_carry_instructions(
|
|
152
|
+
self, carry: Mapping[str, Any],
|
|
153
|
+
) -> list[str]:
|
|
154
|
+
"""이전 계획의 번호를 다시 선언하게 하는 지시.
|
|
155
|
+
|
|
156
|
+
ADR-0015 는 stage 번호를 append-only 로 두고, 계획 파서는 발행된 모든
|
|
157
|
+
계획의 행이 1..N 이기를 요구한다(`stage_map._validate_stage_numbers`).
|
|
158
|
+
이전 stage 를 싣지 않은 리포트는 두 규칙을 동시에 만족시킬 수 없어,
|
|
159
|
+
`okstra prepare` 가 구현 run 을 거절하고 Stage 원장이 읽히지 않는다.
|
|
160
|
+
"""
|
|
161
|
+
taken = [s for s in carry.get("stages", []) if isinstance(s, int)]
|
|
162
|
+
done = [s for s in carry.get("doneStages", []) if isinstance(s, int)]
|
|
163
|
+
if not taken:
|
|
164
|
+
return []
|
|
165
|
+
reauthorable = [stage for stage in taken if stage not in set(done)]
|
|
166
|
+
lines = [
|
|
167
|
+
"This task already has a plan on disk, so stage number(s) "
|
|
168
|
+
f"{', '.join(str(s) for s in taken)} are taken. "
|
|
169
|
+
"`implementationPlanning.stages` and `implementationPlanning.stageMap` "
|
|
170
|
+
"MUST declare every one of them in ascending order with no gap, and a "
|
|
171
|
+
f"stage you add takes the next number after {max(taken)}. A report whose "
|
|
172
|
+
"rows do not start at stage 1 is refused by report assembly and by "
|
|
173
|
+
"`okstra prepare` for the implementation run.",
|
|
174
|
+
]
|
|
175
|
+
if done:
|
|
176
|
+
lines.append(
|
|
177
|
+
f"Stage(s) {', '.join(str(s) for s in done)} are already implemented: "
|
|
178
|
+
f"copy their rows from the \"{CARRIED_STAGES_LABEL}\" source unchanged "
|
|
179
|
+
"- every field, byte for byte - and do not fold their work into a new "
|
|
180
|
+
"stage."
|
|
181
|
+
+ (
|
|
182
|
+
" Stage(s) "
|
|
183
|
+
f"{', '.join(str(s) for s in reauthorable)} are not implemented, so "
|
|
184
|
+
"you may re-author their bodies in place, keeping their numbers."
|
|
185
|
+
if reauthorable
|
|
186
|
+
else ""
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
return lines
|
|
190
|
+
|
|
123
191
|
def _schema_instructions(self) -> list[str]:
|
|
124
192
|
"""스키마가 이 task type 에 못 박은 값을 저작 계약 문장으로 옮긴다."""
|
|
125
193
|
lines: list[str] = []
|
|
@@ -161,7 +229,11 @@ class ReportSynthesisPacket:
|
|
|
161
229
|
return lines
|
|
162
230
|
|
|
163
231
|
def _carry_validation_rules(self) -> list[str]:
|
|
164
|
-
|
|
232
|
+
if self.incremental_decision:
|
|
233
|
+
return ["carried-stages-unchanged"]
|
|
234
|
+
if self.prior_plan_carry:
|
|
235
|
+
return ["prior-stage-numbers-declared"]
|
|
236
|
+
return []
|
|
165
237
|
|
|
166
238
|
def _task_instructions(self) -> list[str]:
|
|
167
239
|
if self.task_type == "technical-verification":
|
|
@@ -623,6 +695,52 @@ def carried_stage_rows(prior_data: Mapping[str, Any], carry_stages: list) -> lis
|
|
|
623
695
|
]
|
|
624
696
|
|
|
625
697
|
|
|
698
|
+
def read_prior_plan_carry(
|
|
699
|
+
project_root: Path, manifest: Mapping[str, Any],
|
|
700
|
+
) -> dict[str, Any] | None:
|
|
701
|
+
"""증분이 아닌 재계획이 이어받아야 하는 이전 계획의 stage 사실. 없으면 ``None``.
|
|
702
|
+
|
|
703
|
+
증분 판정은 "무엇을 다시 검증하지 않을지" 를 말하고, 이건 "어떤 번호가 이미
|
|
704
|
+
점유됐는지" 를 말한다. 후자는 판정의 mode 와 무관하게 참이다 — 선택 방향이
|
|
705
|
+
바뀐 full 재계획도 stage 6 부터 붙여야 하고, 그러려면 1..5 를 다시 선언해야
|
|
706
|
+
한다(ADR-0015 + `stage_map._validate_stage_numbers`).
|
|
707
|
+
"""
|
|
708
|
+
if _string(manifest.get("taskType")) != "implementation-planning":
|
|
709
|
+
return None
|
|
710
|
+
task_root = _string(manifest.get("taskRootPath"))
|
|
711
|
+
if not task_root:
|
|
712
|
+
return None
|
|
713
|
+
ledger = build_stage_ledger(_resolve(project_root, task_root))
|
|
714
|
+
# `None` 은 첫 계획 run, `unreadable` 은 계획을 못 읽은 것이다. 후자에서
|
|
715
|
+
# 번호를 주장하면 읽지 못한 파일을 근거로 지시하는 셈이고, 그 사실은
|
|
716
|
+
# analysis packet 이 이미 통지한다.
|
|
717
|
+
if not ledger or ledger.get("unreadable"):
|
|
718
|
+
return None
|
|
719
|
+
records = [
|
|
720
|
+
row
|
|
721
|
+
for row in ledger.get("stages") or ()
|
|
722
|
+
if isinstance(row, Mapping) and isinstance(row.get("stage"), int)
|
|
723
|
+
]
|
|
724
|
+
if not records:
|
|
725
|
+
return None
|
|
726
|
+
return {
|
|
727
|
+
"sourcePlanPath": _string(ledger.get("latestPlan")),
|
|
728
|
+
"stages": sorted(row["stage"] for row in records),
|
|
729
|
+
"doneStages": sorted(
|
|
730
|
+
row["stage"] for row in records if row.get("status") == "done"
|
|
731
|
+
),
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
@dataclass(frozen=True)
|
|
736
|
+
class CarriedStages:
|
|
737
|
+
"""이월 source 파일과 그것을 만든 근거. 둘 중 하나만 채워진다."""
|
|
738
|
+
|
|
739
|
+
source_path: str
|
|
740
|
+
incremental_decision: dict[str, Any] | None = None
|
|
741
|
+
prior_plan: dict[str, Any] | None = None
|
|
742
|
+
|
|
743
|
+
|
|
626
744
|
def carried_stages_path(narrative_path: Path) -> Path:
|
|
627
745
|
_, markdown_path = report_synthesis_packet_paths(narrative_path)
|
|
628
746
|
return markdown_path.with_name(
|
|
@@ -637,24 +755,60 @@ def materialize_carried_stages(
|
|
|
637
755
|
project_root: Path,
|
|
638
756
|
manifest: Mapping[str, Any],
|
|
639
757
|
narrative_path: Path,
|
|
640
|
-
) ->
|
|
641
|
-
"""이월 stage 본문을 파일로 남기고
|
|
758
|
+
) -> CarriedStages | None:
|
|
759
|
+
"""이월 stage 본문을 파일로 남기고 그 근거와 함께 돌려준다.
|
|
642
760
|
|
|
643
761
|
묶음의 source 는 파일과 그 다이제스트다. 파생 내용을 특례로 인라인하지 않고
|
|
644
762
|
실제 파일로 만드는 이유는 두 가지다 — 다이제스트가 다른 source 와 같은
|
|
645
763
|
의미를 갖고, 어긋났을 때 사람이 열어 볼 수 있다.
|
|
764
|
+
|
|
765
|
+
이월 근거는 둘이고 증분 판정이 이긴다. 증분이면 검증을 건너뛰는 stage 의
|
|
766
|
+
본문이 필요하고, 아니면 번호를 점유한 stage 전부의 본문이 필요하다.
|
|
646
767
|
"""
|
|
647
768
|
decision = read_incremental_decision(project_root, manifest)
|
|
648
|
-
if decision is None:
|
|
769
|
+
if decision is not None:
|
|
770
|
+
return _materialize_stage_rows(
|
|
771
|
+
project_root=project_root,
|
|
772
|
+
narrative_path=narrative_path,
|
|
773
|
+
source_plan=_string(decision.get("prevDataPath")),
|
|
774
|
+
stages=[
|
|
775
|
+
stage for stage in decision["carryStages"] if isinstance(stage, int)
|
|
776
|
+
],
|
|
777
|
+
carried=CarriedStages("", incremental_decision=decision),
|
|
778
|
+
)
|
|
779
|
+
prior_plan = read_prior_plan_carry(project_root, manifest)
|
|
780
|
+
if prior_plan is None:
|
|
649
781
|
return None
|
|
650
|
-
|
|
782
|
+
# 본문이 동결되는 것은 완료된 stage 뿐이다. 완료가 없으면 옮길 행이 없고,
|
|
783
|
+
# 빈 source 를 실으면 작성자가 옮길 것 없는 파일을 읽는다 — 번호를 이어받는
|
|
784
|
+
# 지시만 남긴다.
|
|
785
|
+
if not prior_plan["doneStages"]:
|
|
786
|
+
return CarriedStages("", prior_plan=prior_plan)
|
|
787
|
+
return _materialize_stage_rows(
|
|
788
|
+
project_root=project_root,
|
|
789
|
+
narrative_path=narrative_path,
|
|
790
|
+
source_plan=_string(prior_plan.get("sourcePlanPath")),
|
|
791
|
+
stages=list(prior_plan["doneStages"]),
|
|
792
|
+
carried=CarriedStages("", prior_plan=prior_plan),
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def _materialize_stage_rows(
|
|
797
|
+
*,
|
|
798
|
+
project_root: Path,
|
|
799
|
+
narrative_path: Path,
|
|
800
|
+
source_plan: str,
|
|
801
|
+
stages: list[int],
|
|
802
|
+
carried: CarriedStages,
|
|
803
|
+
) -> CarriedStages | None:
|
|
804
|
+
prior = _resolve(project_root, source_plan)
|
|
651
805
|
if not prior.is_file():
|
|
652
806
|
return None
|
|
653
807
|
try:
|
|
654
808
|
prior_data = load_owned_object(prior, artifact="prior planning report")
|
|
655
809
|
except (OSError, UnicodeError, JsonBoundaryError):
|
|
656
810
|
return None
|
|
657
|
-
rows = carried_stage_rows(prior_data,
|
|
811
|
+
rows = carried_stage_rows(prior_data, stages)
|
|
658
812
|
if not rows:
|
|
659
813
|
return None
|
|
660
814
|
path = carried_stages_path(narrative_path)
|
|
@@ -662,16 +816,21 @@ def materialize_carried_stages(
|
|
|
662
816
|
path,
|
|
663
817
|
{
|
|
664
818
|
"schemaVersion": "1.0",
|
|
665
|
-
"sourcePlanPath":
|
|
666
|
-
"carryStages": list(
|
|
819
|
+
"sourcePlanPath": source_plan,
|
|
820
|
+
"carryStages": list(stages),
|
|
667
821
|
"stages": rows,
|
|
668
822
|
},
|
|
669
823
|
artifact="carried plan stages",
|
|
670
824
|
)
|
|
671
825
|
try:
|
|
672
|
-
|
|
826
|
+
relative = str(path.resolve().relative_to(project_root.resolve()))
|
|
673
827
|
except ValueError:
|
|
674
|
-
|
|
828
|
+
relative = str(path)
|
|
829
|
+
return CarriedStages(
|
|
830
|
+
relative,
|
|
831
|
+
incremental_decision=carried.incremental_decision,
|
|
832
|
+
prior_plan=carried.prior_plan,
|
|
833
|
+
)
|
|
675
834
|
|
|
676
835
|
|
|
677
836
|
def _source_specs(
|
|
@@ -878,7 +1037,7 @@ def build_report_synthesis_packet(
|
|
|
878
1037
|
active_context,
|
|
879
1038
|
team_state,
|
|
880
1039
|
narrative_path,
|
|
881
|
-
carried
|
|
1040
|
+
carried.source_path if carried else "",
|
|
882
1041
|
),
|
|
883
1042
|
)
|
|
884
1043
|
task_type = _string(manifest.get("taskType"))
|
|
@@ -918,7 +1077,8 @@ def build_report_synthesis_packet(
|
|
|
918
1077
|
accounting_snapshot=_accounting_snapshot(team_state),
|
|
919
1078
|
original_requirement_ids=original_requirement_ids,
|
|
920
1079
|
participating_analysers=participating_analysers,
|
|
921
|
-
incremental_decision=carried
|
|
1080
|
+
incremental_decision=carried.incremental_decision if carried else None,
|
|
1081
|
+
prior_plan_carry=carried.prior_plan if carried else None,
|
|
922
1082
|
)
|
|
923
1083
|
|
|
924
1084
|
|
|
@@ -182,6 +182,7 @@ STRUCTURAL_KEYS = frozenset({
|
|
|
182
182
|
"area", # slug-shaped ('scan-coverage')
|
|
183
183
|
"artifact",
|
|
184
184
|
"baseBranch",
|
|
185
|
+
"baseKind",
|
|
185
186
|
"baseRefSha",
|
|
186
187
|
"baseSha",
|
|
187
188
|
"boundary",
|
|
@@ -194,7 +195,6 @@ STRUCTURAL_KEYS = frozenset({
|
|
|
194
195
|
"clarificationId",
|
|
195
196
|
"classification",
|
|
196
197
|
"claudeCode",
|
|
197
|
-
"collectorBranch",
|
|
198
198
|
"command",
|
|
199
199
|
"commandOrObservation",
|
|
200
200
|
"commandOrTest",
|
|
@@ -232,6 +232,7 @@ STRUCTURAL_KEYS = frozenset({
|
|
|
232
232
|
"gitStatusShort",
|
|
233
233
|
"h1c",
|
|
234
234
|
"h2",
|
|
235
|
+
"headBranch",
|
|
235
236
|
"id",
|
|
236
237
|
"impact", # 'high' / 'medium' alongside prose
|
|
237
238
|
"impactKind",
|
|
@@ -10,9 +10,11 @@ fontsninja-v3-site dev-10628-3 implementation-option-selection, grok 리드):
|
|
|
10
10
|
한 줄을 남겼고 HTML 은 영어로 남았다.
|
|
11
11
|
|
|
12
12
|
이 모듈은 그 절차를 `report-finalize` 의 한 단계로 내린다. 예약이 없으면 지시문과
|
|
13
|
-
프롬프트를 발행하고(`agent-prompt materialize` 와 같은 코드),
|
|
14
|
-
|
|
15
|
-
`
|
|
13
|
+
프롬프트를 발행하고(`agent-prompt materialize` 와 같은 코드), 워커를 띄운 뒤
|
|
14
|
+
사이드카가 생겼는지 본다. cmux run 이면 다른 워커와 같은 pane 으로 띄우고 끝날
|
|
15
|
+
때까지 기다린다(`team dispatch` + `team await` 와 같은 코드). 그 밖의 run 은 CLI
|
|
16
|
+
래퍼 디스패처로 띄운다. 디스패치 기록과 결과 링크는 `dispatch_core` 가 배치 안에서
|
|
17
|
+
이미 하므로 여기서 다시 하지 않는다.
|
|
16
18
|
"""
|
|
17
19
|
from __future__ import annotations
|
|
18
20
|
|
|
@@ -25,7 +27,15 @@ from typing import Any, Mapping
|
|
|
25
27
|
|
|
26
28
|
from . import worker_dispatch
|
|
27
29
|
from .agent.prompt_cli.cli import main as agent_prompt_main
|
|
28
|
-
from .
|
|
30
|
+
from .application.dispatch_assignments import dispatch_assignments
|
|
31
|
+
from .dispatch_core import (
|
|
32
|
+
BACKEND_CMUX_PANE,
|
|
33
|
+
await_dispatches,
|
|
34
|
+
dispatch_plan,
|
|
35
|
+
translator_reservations,
|
|
36
|
+
)
|
|
37
|
+
from .ports.worker_dispatch import WorkerDispatchRequest
|
|
38
|
+
from .team import team_dispatch_port
|
|
29
39
|
from .dispatch_state import (
|
|
30
40
|
DispatchError,
|
|
31
41
|
load_json_object,
|
|
@@ -111,7 +121,9 @@ def translate_report(
|
|
|
111
121
|
metadata_path = ensure_translator_reservation(
|
|
112
122
|
project_root, manifest_path, manifest, lang
|
|
113
123
|
)
|
|
114
|
-
dispatch = dispatch_translator(
|
|
124
|
+
dispatch = dispatch_translator(
|
|
125
|
+
project_root, workspace_root, manifest_path, manifest
|
|
126
|
+
)
|
|
115
127
|
except TranslateError as exc:
|
|
116
128
|
return TranslateOutcome(1, "", str(exc))
|
|
117
129
|
if sidecar.is_file():
|
|
@@ -166,13 +178,22 @@ def ensure_translator_reservation(
|
|
|
166
178
|
|
|
167
179
|
|
|
168
180
|
def dispatch_translator(
|
|
169
|
-
project_root: Path,
|
|
181
|
+
project_root: Path,
|
|
182
|
+
workspace_root: Path,
|
|
183
|
+
manifest_path: Path,
|
|
184
|
+
manifest: Mapping[str, Any],
|
|
170
185
|
) -> TranslateOutcome:
|
|
171
|
-
"""
|
|
186
|
+
"""번역 워커를 띄우고 끝날 때까지 기다린다.
|
|
172
187
|
|
|
173
|
-
|
|
174
|
-
|
|
188
|
+
cmux run 은 pane 으로 띄운다 — 사용자가 다른 워커처럼 진행을 화면에서 본다.
|
|
189
|
+
그 밖의 run 은 `okstra worker-dispatch --workers translator` 와 같은 코드를
|
|
190
|
+
프로세스 안에서 돈다. 디스패처는 결과 JSON 을 stdout 에 찍는다. Phase 7 의
|
|
191
|
+
stdout 은 finalize 결과 JSON 하나여야 하므로 여기서 받아 단계 결과로 넘긴다.
|
|
175
192
|
"""
|
|
193
|
+
if str(manifest.get("terminalBackend") or "") == BACKEND_CMUX_PANE:
|
|
194
|
+
return _dispatch_translator_pane(
|
|
195
|
+
project_root, workspace_root, manifest_path, manifest
|
|
196
|
+
)
|
|
176
197
|
out, err = io.StringIO(), io.StringIO()
|
|
177
198
|
with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
|
|
178
199
|
try:
|
|
@@ -187,6 +208,45 @@ def dispatch_translator(
|
|
|
187
208
|
return TranslateOutcome(code, out.getvalue(), err.getvalue())
|
|
188
209
|
|
|
189
210
|
|
|
211
|
+
def _dispatch_translator_pane(
|
|
212
|
+
project_root: Path,
|
|
213
|
+
workspace_root: Path,
|
|
214
|
+
manifest_path: Path,
|
|
215
|
+
manifest: Mapping[str, Any],
|
|
216
|
+
) -> TranslateOutcome:
|
|
217
|
+
"""`okstra team dispatch --workers translator` 뒤 `okstra team await` 와 같다.
|
|
218
|
+
|
|
219
|
+
pane 을 열지 못하면 디스패처가 CLI 래퍼로 강등하고 `DEGRADED` 줄과
|
|
220
|
+
`degradedReason` 을 남긴다 — 그 경우도 여기서 같은 대기로 정산된다. pane 은
|
|
221
|
+
run 끝의 `okstra team teardown` 이 기록을 보고 닫는다.
|
|
222
|
+
"""
|
|
223
|
+
request = WorkerDispatchRequest(
|
|
224
|
+
project_root=project_root,
|
|
225
|
+
run_manifest=manifest_path,
|
|
226
|
+
workspace_root=workspace_root,
|
|
227
|
+
okstra_bin=None,
|
|
228
|
+
requested_workers=(TRANSLATOR_WORKER_ID,),
|
|
229
|
+
idle_timeout_seconds=None,
|
|
230
|
+
dispatch_kind="initial",
|
|
231
|
+
jobs_file=None,
|
|
232
|
+
)
|
|
233
|
+
out, err = io.StringIO(), io.StringIO()
|
|
234
|
+
with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
|
|
235
|
+
try:
|
|
236
|
+
plan = dispatch_assignments(request, team_dispatch_port(manifest))
|
|
237
|
+
backend_plan = plan.backend_plan
|
|
238
|
+
if backend_plan is None:
|
|
239
|
+
raise TranslateError(
|
|
240
|
+
"translator pane dispatch did not produce a backend plan"
|
|
241
|
+
)
|
|
242
|
+
code = dispatch_plan(backend_plan, wait=False)
|
|
243
|
+
if code == 0:
|
|
244
|
+
code = await_dispatches(backend_plan, heartbeat_seconds=0)
|
|
245
|
+
except (DispatchError, OSError) as exc:
|
|
246
|
+
raise TranslateError(f"translator dispatch failed: {exc}") from exc
|
|
247
|
+
return TranslateOutcome(code, out.getvalue(), err.getvalue())
|
|
248
|
+
|
|
249
|
+
|
|
190
250
|
def _materialize_translator(
|
|
191
251
|
project_root: Path,
|
|
192
252
|
manifest_path: Path,
|