okstra 0.202.0 → 0.205.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 +7 -6
- 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/memory/memory.mjs +41 -8
- package/dist/commands/memory/memory.mjs.map +1 -1
- 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 +14 -11
- package/docs/architecture.md +26 -20
- package/docs/cli.md +15 -12
- package/docs/contributor-change-matrix.md +3 -2
- package/docs/performance-improvement-plan-v2.md +3 -9
- package/docs/project-structure-overview.md +39 -11
- 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-option-selection.md +1 -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 +2 -1
- 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-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-executor.md +4 -1
- package/runtime/prompts/profiles/_implementation-self-check.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- 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 +4 -23
- 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 +4 -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 +74 -74
- 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 +14 -18
- 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 +23 -8
- 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_engine.py +38 -0
- package/runtime/python/okstra_ctl/convergence_provenance.py +7 -1
- 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/handoff.py +384 -286
- package/runtime/python/okstra_ctl/handoff_verification.py +25 -6
- 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 +92 -4
- package/runtime/python/okstra_ctl/manager_launch.py +1 -1
- package/runtime/python/okstra_ctl/manager_paths.py +14 -3
- package/runtime/python/okstra_ctl/manager_store.py +210 -3
- package/runtime/python/okstra_ctl/manager_sync.py +4 -1
- package/runtime/python/okstra_ctl/manager_view.py +2 -1
- 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/profile_show.py +42 -1
- 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 +79 -0
- package/runtime/python/okstra_ctl/report_contract.py +1 -1
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +21 -3
- 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/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 +12 -21
- 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 +18 -2
- 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 +31 -12
- package/runtime/skills/okstra-schedule-gen/SKILL.md +19 -14
- package/runtime/skills/okstra-setup/SKILL.md +12 -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 +1 -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 +5 -4
- package/runtime/templates/reports/html/i18n/ko.json +5 -4
- 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 +70 -0
- package/runtime/validators/validate-implementation-plan-stages.py +2 -1
- package/runtime/validators/validate-run.py +72 -15
- 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 -86
- 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 -167
- 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
|
@@ -96,6 +96,7 @@ from .initial_prompt_materialization import (
|
|
|
96
96
|
InitialPromptMaterializationRequest,
|
|
97
97
|
InitialPromptWorkerRequest,
|
|
98
98
|
PromptDeliveryMode,
|
|
99
|
+
declared_delivery_mode,
|
|
99
100
|
materialize_initial_prompts,
|
|
100
101
|
)
|
|
101
102
|
from .agent.invocation import AgentInvocationError, materialize_retry_invocation
|
|
@@ -183,6 +184,9 @@ def revalidate_role_execution(
|
|
|
183
184
|
interaction_surface="role-execution-dispatch",
|
|
184
185
|
),
|
|
185
186
|
environment=host,
|
|
187
|
+
# 이 run 은 이 모델을 이미 골랐다. 지금 고를 수 있는지가 아니라 그
|
|
188
|
+
# 배정이 그대로 실행 가능한지를 묻는 자리다.
|
|
189
|
+
require_selectable=False,
|
|
186
190
|
)
|
|
187
191
|
binding = assignment.binding
|
|
188
192
|
if binding is None:
|
|
@@ -261,6 +265,7 @@ class WorkerHandle:
|
|
|
261
265
|
completed_process: subprocess.CompletedProcess[str] | None
|
|
262
266
|
status_sidecar_path: Path | None
|
|
263
267
|
degraded_from: str
|
|
268
|
+
degraded_reason: str = ""
|
|
264
269
|
running_process: subprocess.Popen[str] | None = None
|
|
265
270
|
mutation_snapshot: MutationSnapshot | None = None
|
|
266
271
|
mutation_policies: tuple[WritePolicy, ...] = ()
|
|
@@ -1439,31 +1444,11 @@ def _invocation_job_fields(
|
|
|
1439
1444
|
def _initial_prompt_delivery_mode(
|
|
1440
1445
|
manifest: Mapping[str, Any],
|
|
1441
1446
|
) -> PromptDeliveryMode:
|
|
1442
|
-
"""이 run 의
|
|
1443
|
-
|
|
1444
|
-
선언은 `leadAdapter.initialPromptDeliveryMode` 에 있고, 그 값은 render 가
|
|
1445
|
-
호스트 descriptor(어댑터가 워커 디스패치를 가져갔으면 그 어댑터)에서
|
|
1446
|
-
옮겨 적은 것이다. 선언이 없는 매니페스트는 이 필드가 생기기 전에 준비된
|
|
1447
|
-
run 이므로 그때의 동작인 `eager-include` 로 간다. 아는 값이 아니면 멈춘다 —
|
|
1448
|
-
프롬프트를 발행하고 나면 그 프롬프트는 불변이라, 잘못 고른 배송 방식은
|
|
1449
|
-
나중에 고칠 수 없다.
|
|
1450
|
-
"""
|
|
1451
|
-
adapter = manifest.get("leadAdapter")
|
|
1452
|
-
declared = (
|
|
1453
|
-
_string_value(adapter.get("initialPromptDeliveryMode"))
|
|
1454
|
-
if isinstance(adapter, Mapping)
|
|
1455
|
-
else ""
|
|
1456
|
-
)
|
|
1457
|
-
if not declared:
|
|
1458
|
-
return PromptDeliveryMode.EAGER_INCLUDE
|
|
1447
|
+
"""이 run 의 초기 프롬프트 배송 방식. 판정은 `declared_delivery_mode` 가 한다."""
|
|
1459
1448
|
try:
|
|
1460
|
-
return
|
|
1461
|
-
except
|
|
1462
|
-
|
|
1463
|
-
raise DispatchError(
|
|
1464
|
-
"leadAdapter.initialPromptDeliveryMode is not a delivery mode: "
|
|
1465
|
-
f"{declared!r} (allowed: {allowed})"
|
|
1466
|
-
) from None
|
|
1449
|
+
return declared_delivery_mode(manifest)
|
|
1450
|
+
except InitialPromptMaterializationError as exc:
|
|
1451
|
+
raise DispatchError(str(exc)) from None
|
|
1467
1452
|
|
|
1468
1453
|
|
|
1469
1454
|
def _materialize_roster_prompts(
|
|
@@ -2034,8 +2019,20 @@ def _start_job(plan: DispatchPlan, job: WorkerJob) -> WorkerHandle:
|
|
|
2034
2019
|
raise DispatchError(str(exc)) from exc
|
|
2035
2020
|
degraded_from = job.backend if runtime.surface != job.backend else ""
|
|
2036
2021
|
settled = replace(job, backend=runtime.surface) if degraded_from else job
|
|
2022
|
+
if degraded_from:
|
|
2023
|
+
print(
|
|
2024
|
+
f"DEGRADED {job.role}: {degraded_from} -> {runtime.surface} "
|
|
2025
|
+
f"({runtime.fallback_reason or 'no reason reported'})",
|
|
2026
|
+
flush=True,
|
|
2027
|
+
)
|
|
2037
2028
|
completed = None
|
|
2038
|
-
|
|
2029
|
+
# 강등된 job 은 여기서 기다리지 않는다. `dispatch_plan` 은 wait=True 에서
|
|
2030
|
+
# pane job 을 거절하므로(같은 함수의 pane_backends 검사) 강등은 비차단 배치
|
|
2031
|
+
# 에서만 일어난다. 그 배치에서 기다리면 라운드가 직렬화되고 — 실측 2026-09-21,
|
|
2032
|
+
# 워커 3명이 병렬 대신 37분 — dispatch 행이 워커가 끝난 뒤에야 써져
|
|
2033
|
+
# `okstra worker-liveness` 가 실행 중인 워커를 모른다. 정산은 상태 사이드카를
|
|
2034
|
+
# 읽는 `await_dispatches` 가 맡는다.
|
|
2035
|
+
if runtime.waitable and not degraded_from:
|
|
2039
2036
|
from .adapters.runtime.assembly import port_for
|
|
2040
2037
|
returncode = port_for(plan.runtime_chain, runtime.surface).wait(runtime)
|
|
2041
2038
|
completed = subprocess.CompletedProcess(list(settled.command), returncode)
|
|
@@ -2046,6 +2043,7 @@ def _start_job(plan: DispatchPlan, job: WorkerJob) -> WorkerHandle:
|
|
|
2046
2043
|
completed,
|
|
2047
2044
|
status_path_for_prompt(settled.prompt_path),
|
|
2048
2045
|
degraded_from,
|
|
2046
|
+
degraded_reason=runtime.fallback_reason,
|
|
2049
2047
|
runtime_handle=runtime,
|
|
2050
2048
|
started_at=started_at,
|
|
2051
2049
|
)
|
|
@@ -2237,9 +2235,28 @@ def _retry_from_record(
|
|
|
2237
2235
|
# 사유 한 줄에 실을 경로 개수. 전부 실으면 team-state 행이 부풀고, 하나도 안
|
|
2238
2236
|
# 실으면 읽는 쪽이 다른 파일을 뒤져야 한다.
|
|
2239
2237
|
_MUTATION_REASON_PATH_LIMIT = 5
|
|
2238
|
+
# 사유는 team-state 의 한 행이다. 래퍼 실패 문장은 길 수 있으므로 자른다.
|
|
2239
|
+
_MUTATION_REASON_FAILURE_LIMIT = 200
|
|
2240
|
+
|
|
2241
|
+
|
|
2242
|
+
def _wrapper_failure_text(outcome: WorkerOutcome | None) -> str:
|
|
2243
|
+
"""워커 래퍼가 남긴 실패 문장. 없으면 빈 문자열.
|
|
2244
|
+
|
|
2245
|
+
`mutation-present-unresolved` 는 "시도가 실패했는데 소스가 바뀌어 있다" 는
|
|
2246
|
+
뜻이고, 왜 실패했는지는 상태 사이드카에만 있었다. 읽는 쪽은 상태 이름만 보고
|
|
2247
|
+
소스를 뒤져야 했다 — 실측 2026-09-23: 두 세션이 같은 자리에서 그렇게
|
|
2248
|
+
진단했고, 원인은 카탈로그에 없는 served 모델이었다.
|
|
2249
|
+
"""
|
|
2250
|
+
if outcome is None or outcome.status_sidecar_path is None:
|
|
2251
|
+
return ""
|
|
2252
|
+
status = read_wrapper_status(outcome.status_sidecar_path)
|
|
2253
|
+
if status is None:
|
|
2254
|
+
return ""
|
|
2255
|
+
failure = _string_value(status.raw.get("failure"))
|
|
2256
|
+
return failure[:_MUTATION_REASON_FAILURE_LIMIT]
|
|
2240
2257
|
|
|
2241
2258
|
|
|
2242
|
-
def _mutation_failure_reason(mutation) -> str:
|
|
2259
|
+
def _mutation_failure_reason(mutation, outcome: WorkerOutcome | None = None) -> str:
|
|
2243
2260
|
"""왜 실패했는지와 무엇이 바뀌었는지를 한 줄에 담는다.
|
|
2244
2261
|
|
|
2245
2262
|
종전에는 디스패치 행에 `readonly source changed` 만 남고 바뀐 경로는
|
|
@@ -2249,6 +2266,9 @@ def _mutation_failure_reason(mutation) -> str:
|
|
|
2249
2266
|
사유 옆에 놓는다.
|
|
2250
2267
|
"""
|
|
2251
2268
|
reason = "; ".join(mutation.violations) or mutation.status
|
|
2269
|
+
failure = _wrapper_failure_text(outcome)
|
|
2270
|
+
if failure:
|
|
2271
|
+
reason = f"{reason}: {failure}"
|
|
2252
2272
|
paths = list(mutation.changed_paths)
|
|
2253
2273
|
if not paths:
|
|
2254
2274
|
return reason
|
|
@@ -2273,7 +2293,7 @@ def _finish_attempt(
|
|
|
2273
2293
|
"contract-failed-unattributed",
|
|
2274
2294
|
"mutation-present-unresolved",
|
|
2275
2295
|
}:
|
|
2276
|
-
reason = _mutation_failure_reason(mutation)
|
|
2296
|
+
reason = _mutation_failure_reason(mutation, outcome)
|
|
2277
2297
|
_update_dispatch_status(
|
|
2278
2298
|
plan.team_state_path, job, attempt, "error", reason
|
|
2279
2299
|
)
|
|
@@ -2913,6 +2933,7 @@ def _record_dispatch(
|
|
|
2913
2933
|
handle.pane_id,
|
|
2914
2934
|
handle.degraded_from,
|
|
2915
2935
|
reason,
|
|
2936
|
+
degraded_reason=handle.degraded_reason,
|
|
2916
2937
|
mutation_snapshot_path=(
|
|
2917
2938
|
_mutation_snapshot_path(handle.job)
|
|
2918
2939
|
if handle.mutation_snapshot is not None
|
|
@@ -2932,6 +2953,7 @@ def _dispatch_record(
|
|
|
2932
2953
|
degraded_from: str,
|
|
2933
2954
|
reason: str = "",
|
|
2934
2955
|
*,
|
|
2956
|
+
degraded_reason: str = "",
|
|
2935
2957
|
mutation_snapshot_path: Path | None = None,
|
|
2936
2958
|
write_policy_digest: str = "",
|
|
2937
2959
|
write_enforcement: WriteEnforcement | None = None,
|
|
@@ -2958,6 +2980,9 @@ def _dispatch_record(
|
|
|
2958
2980
|
),
|
|
2959
2981
|
"statusSidecarPath": str(status_path_for_prompt(job.prompt_path)),
|
|
2960
2982
|
"degradedFrom": degraded_from,
|
|
2983
|
+
# `reason` 은 이 행의 정산 사유라 마감과 teardown 이 덮어쓴다. 강등 사유는
|
|
2984
|
+
# 디스패치 시점의 사실이므로 자기 칸을 쓴다.
|
|
2985
|
+
"degradedReason": degraded_reason,
|
|
2961
2986
|
"reason": reason,
|
|
2962
2987
|
"modelExecutionValue": job.model_execution_value,
|
|
2963
2988
|
"wrapperPath": str(job.wrapper_path),
|
|
@@ -3186,6 +3211,7 @@ def _event_details(job: WorkerJob, handle: WorkerHandle) -> dict[str, Any]:
|
|
|
3186
3211
|
"role": job.role,
|
|
3187
3212
|
"paneId": handle.pane_id,
|
|
3188
3213
|
"degradedFrom": handle.degraded_from,
|
|
3214
|
+
"degradedReason": handle.degraded_reason,
|
|
3189
3215
|
}
|
|
3190
3216
|
return details
|
|
3191
3217
|
|
|
@@ -51,6 +51,11 @@ class HostDescriptor:
|
|
|
51
51
|
session_accounting: str
|
|
52
52
|
has_claude_session: bool
|
|
53
53
|
relay_contract: str
|
|
54
|
+
# 이 호스트 세션 안에서 도는 워커(runner=native-session)가 읽어야 하는
|
|
55
|
+
# 계약 파일의 절대 경로. 호스트가 그런 계약을 선언하지 않으면 빈 문자열이다.
|
|
56
|
+
# 호스트 전역 발견 경로에 설치되지 않는다 — 디스패치 프롬프트가 경로로
|
|
57
|
+
# 전달하고, 워커가 프리앰블과 같은 방식으로 연다.
|
|
58
|
+
worker_session_contract: str = ""
|
|
54
59
|
# 이 호스트가 초기 워커 프롬프트를 어떻게 싣겠다고 선언하는가. 값은
|
|
55
60
|
# `relay.md` 의 `initialPromptDeliveryMode` 행과 같아야 한다 — 그 표를
|
|
56
61
|
# 읽고 dispatch 를 판단하는 것은 리드이고, 실제로 프롬프트를 만드는 것은
|
|
@@ -31,9 +31,19 @@ class WorkerSpawnRequest:
|
|
|
31
31
|
|
|
32
32
|
@dataclass(frozen=True)
|
|
33
33
|
class RuntimeHandle:
|
|
34
|
+
"""An opened surface, and why it is not the one that was planned.
|
|
35
|
+
|
|
36
|
+
`fallback_reason` is empty when the planned surface opened. It carries the
|
|
37
|
+
last `SurfaceUnavailable` message when an earlier surface in the chain was
|
|
38
|
+
skipped, because that message exists nowhere else: the chain swallows the
|
|
39
|
+
exception it recovers from, and a degraded dispatch used to be recorded
|
|
40
|
+
with an empty reason.
|
|
41
|
+
"""
|
|
42
|
+
|
|
34
43
|
surface: str
|
|
35
44
|
identifier: str
|
|
36
45
|
waitable: bool
|
|
46
|
+
fallback_reason: str = ""
|
|
37
47
|
|
|
38
48
|
|
|
39
49
|
@dataclass(frozen=True)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Read-side aggregator for okstra-run error logs.
|
|
2
2
|
|
|
3
3
|
task 의 모든 run errors-<type>-<seq>.jsonl 을 모아 phase / worker / errorType
|
|
4
|
-
별로 집계하고 timestamp 누적 .md 리포트로 렌더한다.
|
|
5
|
-
|
|
4
|
+
별로 집계하고 timestamp 누적 .md 리포트로 렌더한다. 리포트 외에 쓰는 것은
|
|
5
|
+
task 해석 중 `find_task_root` 가 끝난 implementation 결과를 기록하는 경우뿐이다.
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
@@ -236,7 +236,8 @@ _CLI_EPILOG = r"""Usage:
|
|
|
236
236
|
|
|
237
237
|
Writes a timestamped markdown report under <task-root>/error-reports/ and
|
|
238
238
|
prints a JSON summary (counts by errorType / source / phase / agent) to stdout.
|
|
239
|
-
|
|
239
|
+
Resolving a task may record a finished implementation outcome in its task
|
|
240
|
+
manifest, the task catalog, and consumers.jsonl; nothing else is written.
|
|
240
241
|
"""
|
|
241
242
|
|
|
242
243
|
|
|
@@ -213,7 +213,9 @@ def reserve_derived_role_invocation(
|
|
|
213
213
|
raise ExecutionManifestError(
|
|
214
214
|
f"source role execution is unknown: {source_role_execution_ref}"
|
|
215
215
|
)
|
|
216
|
-
derived, created = _derived_role_execution(
|
|
216
|
+
derived, created, derived_participant = _derived_role_execution(
|
|
217
|
+
manifest, source, role
|
|
218
|
+
)
|
|
217
219
|
invocation = invocation_factory(derived)
|
|
218
220
|
if not isinstance(invocation, Invocation):
|
|
219
221
|
raise ExecutionManifestError("invocation factory must return Invocation")
|
|
@@ -244,14 +246,22 @@ def reserve_derived_role_invocation(
|
|
|
244
246
|
_write_json_atomic(task_manifest_path, recovered_task_payload)
|
|
245
247
|
return derived, existing_invocation
|
|
246
248
|
|
|
249
|
+
base = manifest
|
|
250
|
+
if created and derived_participant is not None:
|
|
251
|
+
base = _replace_manifest(
|
|
252
|
+
manifest,
|
|
253
|
+
participant_assignments=(
|
|
254
|
+
*manifest.participant_assignments,
|
|
255
|
+
derived_participant,
|
|
256
|
+
),
|
|
257
|
+
)
|
|
247
258
|
roles = (
|
|
248
|
-
(*
|
|
249
|
-
if created
|
|
250
|
-
else manifest.role_executions
|
|
259
|
+
(*base.role_executions, derived) if created else base.role_executions
|
|
251
260
|
)
|
|
252
|
-
with_role = _replace_manifest(
|
|
261
|
+
with_role = _replace_manifest(base, role_executions=roles)
|
|
253
262
|
if created:
|
|
254
|
-
|
|
263
|
+
# 새 참가자가 실린 뒤라야 참가자 참조 검사가 성립한다.
|
|
264
|
+
_validate_next_role_execution(base, derived)
|
|
255
265
|
_validate_next_invocation(with_role, invocation)
|
|
256
266
|
updated = _replace_manifest(
|
|
257
267
|
with_role,
|
|
@@ -540,11 +550,13 @@ def _validate_roles(
|
|
|
540
550
|
"sourceRoleExecutionRef must reference an earlier different "
|
|
541
551
|
"role execution"
|
|
542
552
|
)
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
553
|
+
# 참가자는 더 이상 공유하지 않는다(ADR-0017). 파생을 파생이게 하는
|
|
554
|
+
# 것은 같은 모델로 같은 일을 다시 읽는다는 것이므로, 거기를 검사한다.
|
|
555
|
+
for key in ("provider", "modelRef", "modelId"):
|
|
556
|
+
if source_role.get(key) != row.get(key):
|
|
557
|
+
raise ExecutionManifestError(
|
|
558
|
+
f"derived role execution {key} must match its source"
|
|
559
|
+
)
|
|
548
560
|
earlier_roles[role_ref] = row
|
|
549
561
|
for role, values in ordinals.items():
|
|
550
562
|
if sorted(values) != list(range(1, len(values) + 1)):
|
|
@@ -815,20 +827,29 @@ def _derived_role_execution(
|
|
|
815
827
|
manifest: ExecutionManifest,
|
|
816
828
|
source: RoleExecution,
|
|
817
829
|
role: str,
|
|
818
|
-
) -> tuple[RoleExecution, bool]:
|
|
830
|
+
) -> tuple[RoleExecution, bool, ParticipantAssignment | None]:
|
|
831
|
+
"""The dynamic role slot derived from *source*, creating it when absent.
|
|
832
|
+
|
|
833
|
+
A created slot is its own participant. It copies the source's provider,
|
|
834
|
+
model and binding — that is what makes it the same model re-reading the
|
|
835
|
+
same work — but not the source's identity or session. Sharing the
|
|
836
|
+
participant made the derived slot indistinguishable from its source
|
|
837
|
+
wherever a participant is what gets attributed or resumed.
|
|
838
|
+
"""
|
|
819
839
|
if source.role == role:
|
|
820
|
-
return source, False
|
|
840
|
+
return source, False, None
|
|
821
841
|
existing = [
|
|
822
842
|
item
|
|
823
843
|
for item in manifest.role_executions
|
|
824
|
-
if item.
|
|
844
|
+
if item.source_role_execution_ref == source.role_execution_ref
|
|
845
|
+
and item.role == role
|
|
825
846
|
]
|
|
826
847
|
if len(existing) > 1:
|
|
827
848
|
raise ExecutionManifestError(
|
|
828
|
-
f"
|
|
849
|
+
f"source has multiple {role} role executions: {source.role_execution_ref}"
|
|
829
850
|
)
|
|
830
851
|
if existing:
|
|
831
|
-
return existing[0], False
|
|
852
|
+
return existing[0], False, None
|
|
832
853
|
ordinal = max(
|
|
833
854
|
(item.ordinal for item in manifest.role_executions if item.role == role),
|
|
834
855
|
default=0,
|
|
@@ -840,9 +861,10 @@ def _derived_role_execution(
|
|
|
840
861
|
raise ExecutionManifestError(
|
|
841
862
|
f"derived roleExecutionRef is already reserved: {role_ref}"
|
|
842
863
|
)
|
|
864
|
+
participant = _derived_participant(manifest, source)
|
|
843
865
|
return RoleExecution(
|
|
844
866
|
role_execution_ref=role_ref,
|
|
845
|
-
participant_ref=
|
|
867
|
+
participant_ref=participant.participant_ref,
|
|
846
868
|
source_role_execution_ref=source.role_execution_ref,
|
|
847
869
|
role=role,
|
|
848
870
|
provider=source.provider,
|
|
@@ -859,7 +881,38 @@ def _derived_role_execution(
|
|
|
859
881
|
binding=source.binding,
|
|
860
882
|
served_model_attestation=ServedModelAttestation.unknown(),
|
|
861
883
|
status="prepared",
|
|
862
|
-
), True
|
|
884
|
+
), True, participant
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
def _derived_participant(
|
|
888
|
+
manifest: ExecutionManifest,
|
|
889
|
+
source: RoleExecution,
|
|
890
|
+
) -> ParticipantAssignment:
|
|
891
|
+
"""A fresh participant carrying the source's model but none of its session."""
|
|
892
|
+
origin = next(
|
|
893
|
+
(
|
|
894
|
+
item
|
|
895
|
+
for item in manifest.participant_assignments
|
|
896
|
+
if item.participant_ref == source.participant_ref
|
|
897
|
+
),
|
|
898
|
+
None,
|
|
899
|
+
)
|
|
900
|
+
if origin is None:
|
|
901
|
+
raise ExecutionManifestError(
|
|
902
|
+
f"source role execution references unknown participant: "
|
|
903
|
+
f"{source.participant_ref}"
|
|
904
|
+
)
|
|
905
|
+
used = {item.participant_ref for item in manifest.participant_assignments}
|
|
906
|
+
ordinal = len(used) + 1
|
|
907
|
+
while f"participant-{ordinal:03d}" in used:
|
|
908
|
+
ordinal += 1
|
|
909
|
+
return replace(
|
|
910
|
+
origin,
|
|
911
|
+
participant_ref=f"participant-{ordinal:03d}",
|
|
912
|
+
session_ref=None,
|
|
913
|
+
window_ref=None,
|
|
914
|
+
status="prepared",
|
|
915
|
+
)
|
|
863
916
|
|
|
864
917
|
|
|
865
918
|
def _validate_next_invocation(manifest: ExecutionManifest, row: Invocation) -> None:
|