okstra 0.179.2 → 0.183.2
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 +1 -1
- package/dist/cli-registry.mjs +30 -2
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/incremental-carry.mjs +9 -8
- package/dist/commands/execute/incremental-carry.mjs.map +1 -1
- package/dist/commands/execute/plan-verify.mjs +3 -1
- package/dist/commands/execute/plan-verify.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.d.mts +4 -2
- package/dist/commands/execute/render-bundle.mjs +46 -5
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +11 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/inspect/model-io.d.mts +1 -0
- package/dist/commands/inspect/model-io.mjs +25 -0
- package/dist/commands/inspect/model-io.mjs.map +1 -0
- package/dist/commands/inspect/stage-map.mjs +29 -8
- package/dist/commands/inspect/stage-map.mjs.map +1 -1
- package/dist/commands/inspect/task-list.mjs +52 -6
- package/dist/commands/inspect/task-list.mjs.map +1 -1
- package/dist/commands/inspect/user-response.mjs +14 -4
- package/dist/commands/inspect/user-response.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.d.mts +1 -0
- package/dist/commands/lifecycle/check-project.mjs +69 -50
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/contract-check.d.mts +1 -0
- package/dist/commands/lifecycle/contract-check.mjs +18 -0
- package/dist/commands/lifecycle/contract-check.mjs.map +1 -0
- package/dist/commands/lifecycle/preflight.mjs +154 -51
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/pr/pr.d.mts +1 -0
- package/dist/commands/pr/pr.mjs +19 -1
- package/dist/commands/pr/pr.mjs.map +1 -1
- package/dist/commands/report/agent-activity.mjs +2 -2
- package/dist/commands/report/approval-decision.d.mts +1 -0
- package/dist/commands/report/approval-decision.mjs +21 -0
- package/dist/commands/report/approval-decision.mjs.map +1 -0
- package/dist/commands/report/design-snapshot.d.mts +1 -0
- package/dist/commands/report/design-snapshot.mjs +19 -0
- package/dist/commands/report/design-snapshot.mjs.map +1 -0
- package/dist/commands/report/translate.mjs +3 -0
- package/dist/commands/report/translate.mjs.map +1 -1
- package/dist/lib/host-registry-client.mjs +13 -9
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/docs/architecture/storage-model.md +1 -1
- package/docs/architecture.md +21 -10
- package/docs/cli.md +36 -20
- package/docs/container.md +6 -4
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-brief-gen.md +5 -3
- package/docs/for-ai/skills/okstra-code-review.md +4 -4
- package/docs/for-ai/skills/okstra-container-build.md +20 -17
- package/docs/for-ai/skills/okstra-inspect.md +20 -23
- package/docs/for-ai/skills/okstra-manager.md +19 -18
- package/docs/for-ai/skills/okstra-memory.md +2 -2
- package/docs/for-ai/skills/okstra-pr-gen.md +3 -3
- package/docs/for-ai/skills/okstra-rollup.md +14 -13
- package/docs/for-ai/skills/okstra-run.md +7 -3
- package/docs/for-ai/skills/okstra-schedule-gen.md +15 -18
- package/docs/for-ai/skills/okstra-setup.md +7 -7
- package/docs/for-ai/skills/okstra-usage.md +5 -4
- package/docs/for-ai/skills/okstra-user-response.md +50 -32
- package/docs/project-structure-overview.md +43 -31
- package/docs/task-process/implementation-planning.md +2 -2
- package/docs/task-process/implementation.md +7 -4
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +4 -4
- package/runtime/agents/workers/report-writer-worker.md +15 -164
- package/runtime/agents/workers/translator-worker.md +5 -13
- package/runtime/bin/okstra-error-log.py +51 -11
- package/runtime/bin/okstra-report-translate.py +210 -23
- package/runtime/prompts/host-orchestration/implementation.md +1 -1
- package/runtime/prompts/launch.template.md +9 -12
- package/runtime/prompts/lead/adapters/cmux.md +1 -1
- package/runtime/prompts/lead/context-loader.md +41 -141
- package/runtime/prompts/lead/convergence.md +10 -8
- package/runtime/prompts/lead/okstra-lead-contract.md +40 -49
- package/runtime/prompts/lead/plan-body-verification.md +217 -23
- package/runtime/prompts/lead/report-writer.md +81 -421
- package/runtime/prompts/lead/team-contract.md +9 -54
- package/runtime/prompts/profiles/_clarification-recommendation.md +5 -4
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +3 -2
- package/runtime/prompts/profiles/_common-contract.md +4 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/change-impact-analysis.md +1 -1
- package/runtime/prompts/profiles/error-analysis.md +1 -1
- package/runtime/prompts/profiles/feature-analysis.md +1 -1
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +20 -12
- package/runtime/prompts/profiles/improvement-discovery.md +1 -1
- package/runtime/prompts/profiles/project-analysis.md +1 -1
- package/runtime/prompts/profiles/release-handoff.md +0 -1
- package/runtime/prompts/profiles/requirements-discovery.md +1 -1
- package/runtime/prompts/wizard/prompts.ko.json +0 -1
- package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +69 -17
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +13 -4
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +8 -2
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +50 -5
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +66 -5
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +70 -2
- package/runtime/python/okstra_ctl/agent_activity.py +141 -38
- package/runtime/python/okstra_ctl/agent_invocation.py +19 -6
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +70 -23
- package/runtime/python/okstra_ctl/analysis_inputs.py +5 -4
- package/runtime/python/okstra_ctl/analysis_packet.py +124 -3
- package/runtime/python/okstra_ctl/approval_decisions.py +328 -0
- package/runtime/python/okstra_ctl/attempt_evidence.py +2 -2
- package/runtime/python/okstra_ctl/backfill.py +13 -10
- package/runtime/python/okstra_ctl/batch.py +2 -4
- package/runtime/python/okstra_ctl/build_tools.py +6 -3
- package/runtime/python/okstra_ctl/claim_reproduction.py +101 -0
- package/runtime/python/okstra_ctl/clarification_items.py +27 -13
- package/runtime/python/okstra_ctl/cmux.py +130 -52
- package/runtime/python/okstra_ctl/code_review_target.py +34 -8
- package/runtime/python/okstra_ctl/consumers.py +5 -4
- package/runtime/python/okstra_ctl/container.py +103 -8
- package/runtime/python/okstra_ctl/context_cost.py +2 -1
- package/runtime/python/okstra_ctl/contract_graph.py +497 -0
- package/runtime/python/okstra_ctl/contract_graph_cli.py +62 -0
- package/runtime/python/okstra_ctl/convergence.py +338 -17
- package/runtime/python/okstra_ctl/convergence_engine.py +10 -18
- package/runtime/python/okstra_ctl/convergence_provenance.py +58 -8
- package/runtime/python/okstra_ctl/convergence_store.py +55 -34
- package/runtime/python/okstra_ctl/design_prep.py +7 -4
- package/runtime/python/okstra_ctl/design_snapshot.py +134 -0
- package/runtime/python/okstra_ctl/dispatch_core.py +97 -69
- package/runtime/python/okstra_ctl/dispatch_state.py +163 -63
- package/runtime/python/okstra_ctl/doctor.py +6 -3
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +70 -9
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +16 -30
- package/runtime/python/okstra_ctl/error_log_write.py +35 -30
- package/runtime/python/okstra_ctl/error_report.py +26 -1
- package/runtime/python/okstra_ctl/error_zip.py +27 -5
- package/runtime/python/okstra_ctl/execution_identity.py +3 -2
- package/runtime/python/okstra_ctl/execution_manifest.py +7 -4
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +6 -2
- package/runtime/python/okstra_ctl/final_report_schema.py +26 -17
- package/runtime/python/okstra_ctl/fix_cycles.py +2 -2
- package/runtime/python/okstra_ctl/fixed_text.py +39 -0
- package/runtime/python/okstra_ctl/git_reconcile.py +41 -9
- package/runtime/python/okstra_ctl/handoff.py +5 -4
- package/runtime/python/okstra_ctl/i18n.py +4 -2
- package/runtime/python/okstra_ctl/implementation_direction.py +22 -14
- package/runtime/python/okstra_ctl/implementation_outcome.py +4 -7
- package/runtime/python/okstra_ctl/incremental_carry.py +130 -17
- package/runtime/python/okstra_ctl/incremental_scope.py +12 -4
- package/runtime/python/okstra_ctl/index.py +8 -11
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +79 -7
- package/runtime/python/okstra_ctl/invocation.py +3 -6
- package/runtime/python/okstra_ctl/json_boundary.py +366 -0
- package/runtime/python/okstra_ctl/json_registry.py +10 -12
- package/runtime/python/okstra_ctl/jsonl.py +19 -2
- package/runtime/python/okstra_ctl/lead_events.py +33 -1
- package/runtime/python/okstra_ctl/listing.py +3 -3
- package/runtime/python/okstra_ctl/log_report.py +24 -2
- package/runtime/python/okstra_ctl/manager_cli.py +92 -7
- package/runtime/python/okstra_ctl/manager_store.py +12 -10
- package/runtime/python/okstra_ctl/material.py +5 -1
- package/runtime/python/okstra_ctl/migrate.py +29 -25
- package/runtime/python/okstra_ctl/model_cli.py +3 -15
- package/runtime/python/okstra_ctl/model_io_cli.py +1051 -0
- package/runtime/python/okstra_ctl/mutation_probe.py +13 -4
- package/runtime/python/okstra_ctl/pane_reclaim.py +3 -2
- package/runtime/python/okstra_ctl/path_hints.py +12 -0
- package/runtime/python/okstra_ctl/paths.py +21 -0
- package/runtime/python/okstra_ctl/plan_items.py +115 -4
- package/runtime/python/okstra_ctl/plan_items_cli.py +626 -34
- package/runtime/python/okstra_ctl/ports/worker_dispatch.py +2 -1
- package/runtime/python/okstra_ctl/pr_template.py +3 -2
- package/runtime/python/okstra_ctl/project_meta.py +5 -7
- package/runtime/python/okstra_ctl/recap.py +5 -4
- package/runtime/python/okstra_ctl/reconcile.py +21 -27
- package/runtime/python/okstra_ctl/registry/host_discovery.py +3 -2
- package/runtime/python/okstra_ctl/registry/provider_registry.py +3 -2
- package/runtime/python/okstra_ctl/render.py +74 -16
- package/runtime/python/okstra_ctl/render_final_report.py +10 -8
- package/runtime/python/okstra_ctl/report_assembly.py +509 -0
- package/runtime/python/okstra_ctl/report_contract.py +2 -1
- package/runtime/python/okstra_ctl/report_finalize.py +67 -32
- package/runtime/python/okstra_ctl/report_html/render.py +3 -2
- package/runtime/python/okstra_ctl/report_inputs.py +72 -0
- package/runtime/python/okstra_ctl/report_language.py +3 -2
- package/runtime/python/okstra_ctl/report_markdown.py +81 -8
- package/runtime/python/okstra_ctl/report_narrative.py +351 -0
- package/runtime/python/okstra_ctl/report_projections.py +265 -0
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +518 -0
- package/runtime/python/okstra_ctl/report_views.py +3 -2
- package/runtime/python/okstra_ctl/rollup.py +65 -4
- package/runtime/python/okstra_ctl/run.py +184 -65
- package/runtime/python/okstra_ctl/run_audit.py +3 -2
- package/runtime/python/okstra_ctl/run_context.py +6 -9
- package/runtime/python/okstra_ctl/run_index_row.py +2 -8
- package/runtime/python/okstra_ctl/schedule_semantics.py +5 -2
- package/runtime/python/okstra_ctl/schema_excerpt.py +15 -8
- package/runtime/python/okstra_ctl/session_transcript.py +27 -1
- package/runtime/python/okstra_ctl/set_work_status.py +64 -38
- package/runtime/python/okstra_ctl/stage_fix_carry.py +8 -6
- package/runtime/python/okstra_ctl/stage_ledger.py +132 -18
- package/runtime/python/okstra_ctl/stage_map.py +96 -28
- package/runtime/python/okstra_ctl/stage_targets.py +3 -4
- package/runtime/python/okstra_ctl/team.py +3 -2
- package/runtime/python/okstra_ctl/team_reconcile.py +11 -2
- package/runtime/python/okstra_ctl/time_report.py +51 -4
- package/runtime/python/okstra_ctl/usage_identity.py +2 -1
- package/runtime/python/okstra_ctl/usage_report.py +58 -4
- package/runtime/python/okstra_ctl/user_response.py +1431 -66
- package/runtime/python/okstra_ctl/wizard.py +11 -44
- package/runtime/python/okstra_ctl/work_categories.py +3 -2
- package/runtime/python/okstra_ctl/worker_dispatch.py +5 -2
- package/runtime/python/okstra_ctl/worker_prompt_body.py +49 -3
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +3 -2
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +31 -3
- package/runtime/python/okstra_ctl/worker_runner.py +14 -12
- package/runtime/python/okstra_ctl/workflow.py +2 -1
- package/runtime/python/okstra_ctl/worktree.py +3 -2
- package/runtime/python/okstra_ctl/wrapper_status.py +4 -2
- package/runtime/python/okstra_ctl/write_policy.py +4 -2
- package/runtime/python/okstra_token_usage/antigravity.py +39 -12
- package/runtime/python/okstra_token_usage/collect.py +90 -38
- package/runtime/python/okstra_token_usage/grok.py +127 -0
- package/runtime/schemas/final-report-v2.0.schema.json +9 -0
- package/runtime/schemas/final-report-v3.0.schema.json +10219 -0
- package/runtime/schemas/report-narrative-v3.0.schema.json +30 -0
- package/runtime/schemas/report-synthesis-packet-v1.0.schema.json +140 -0
- package/runtime/skills/okstra-brief-gen/SKILL.md +9 -7
- package/runtime/skills/okstra-code-review/SKILL.md +21 -11
- package/runtime/skills/okstra-container-build/SKILL.md +18 -18
- package/runtime/skills/okstra-inspect/SKILL.md +12 -11
- package/runtime/skills/okstra-inspect/facets/error-zip.md +8 -8
- package/runtime/skills/okstra-inspect/facets/errors.md +2 -2
- package/runtime/skills/okstra-inspect/facets/history.md +9 -14
- package/runtime/skills/okstra-inspect/facets/logs.md +2 -2
- package/runtime/skills/okstra-inspect/facets/recap.md +5 -5
- package/runtime/skills/okstra-inspect/facets/report.md +6 -10
- package/runtime/skills/okstra-inspect/facets/status.md +9 -8
- package/runtime/skills/okstra-inspect/facets/time.md +3 -3
- package/runtime/skills/okstra-manager/SKILL.md +16 -14
- package/runtime/skills/okstra-memory/SKILL.md +3 -3
- package/runtime/skills/okstra-pr-gen/SKILL.md +5 -4
- package/runtime/skills/okstra-rollup/SKILL.md +6 -16
- package/runtime/skills/okstra-run/SKILL.md +7 -7
- package/runtime/skills/okstra-schedule-gen/SKILL.md +21 -17
- package/runtime/skills/okstra-setup/SKILL.md +21 -13
- package/runtime/skills/okstra-setup/references/project-config.md +2 -2
- package/runtime/skills/okstra-usage/SKILL.md +10 -10
- package/runtime/skills/okstra-user-response/SKILL.md +78 -107
- package/runtime/templates/report-writer-prompt-preamble.md +29 -19
- package/runtime/templates/reports/html/macros/forms.html +6 -4
- package/runtime/templates/reports/schedule.template.md +4 -4
- package/runtime/templates/worker-error-contract.md +17 -29
- package/runtime/validators/validate-run.py +636 -72
- package/runtime/validators/validate_session_conformance.py +43 -7
|
@@ -85,6 +85,7 @@ from .self_mock_signals import (
|
|
|
85
85
|
partition_waived_entries,
|
|
86
86
|
selfmock_path_key,
|
|
87
87
|
)
|
|
88
|
+
from .json_boundary import JsonBoundaryError, external_tool_json_source, load_external_json
|
|
88
89
|
|
|
89
90
|
# `survived` is read by a human in the sidecar, so it is trimmed. The verdict and
|
|
90
91
|
# the logged total are taken before the trim — the cap shortens the report, never
|
|
@@ -414,8 +415,12 @@ class StrykerAdapter:
|
|
|
414
415
|
if not path.is_file():
|
|
415
416
|
return None
|
|
416
417
|
try:
|
|
417
|
-
|
|
418
|
-
|
|
418
|
+
# 외부 입력: mutation testing 도구가 생성한 실행 보고서다.
|
|
419
|
+
return load_external_json(
|
|
420
|
+
external_tool_json_source(path, Path(worktree) if worktree else Path.cwd()),
|
|
421
|
+
artifact="mutation test report",
|
|
422
|
+
)
|
|
423
|
+
except JsonBoundaryError:
|
|
419
424
|
return None
|
|
420
425
|
|
|
421
426
|
|
|
@@ -1004,8 +1009,12 @@ class CargoMutantsAdapter:
|
|
|
1004
1009
|
if not path.is_file():
|
|
1005
1010
|
return None
|
|
1006
1011
|
try:
|
|
1007
|
-
|
|
1008
|
-
|
|
1012
|
+
# 외부 입력: mutation testing 도구가 생성한 실행 보고서다.
|
|
1013
|
+
data = load_external_json(
|
|
1014
|
+
external_tool_json_source(path, Path(worktree) if worktree else Path.cwd()),
|
|
1015
|
+
artifact="mutation test report",
|
|
1016
|
+
)
|
|
1017
|
+
except JsonBoundaryError:
|
|
1009
1018
|
return None
|
|
1010
1019
|
return data if isinstance(data, dict) else None
|
|
1011
1020
|
|
|
@@ -18,6 +18,7 @@ import sys
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
20
|
from .dispatch_state import NON_TERMINAL_WORKER_STATUSES
|
|
21
|
+
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def _newest_team_state(state_dir: Path) -> Path | None:
|
|
@@ -33,8 +34,8 @@ def _newest_team_state(state_dir: Path) -> Path | None:
|
|
|
33
34
|
|
|
34
35
|
def _has_live_dispatch(team_state_path: Path) -> bool:
|
|
35
36
|
try:
|
|
36
|
-
payload =
|
|
37
|
-
except
|
|
37
|
+
payload = load_owned_object(team_state_path, artifact="team state")
|
|
38
|
+
except JsonBoundaryError:
|
|
38
39
|
return False
|
|
39
40
|
if not isinstance(payload, dict):
|
|
40
41
|
return False
|
|
@@ -439,6 +439,10 @@ def _run_files(
|
|
|
439
439
|
"codex_worker_result": worker_results / f"codex-worker{suffixes['worker_results']}.md",
|
|
440
440
|
"antigravity_worker_result": worker_results / f"antigravity-worker{suffixes['worker_results']}.md",
|
|
441
441
|
"report_writer_worker_result": worker_results / f"report-writer-worker{suffixes['worker_results']}.md",
|
|
442
|
+
"report_writer_narrative": worker_results / f"report-writer-narrative{suffixes['worker_results']}.md",
|
|
443
|
+
"approval_decisions": run_state / f"approval-decisions-{task_type_segment}-{sequences['state']}.json",
|
|
444
|
+
"design_preparation": run_state / f"design-preparation-{task_type_segment}-{sequences['state']}.json",
|
|
445
|
+
"plan_body_verification": run_state / f"plan-body-verification-{task_type_segment}-{sequences['state']}.json",
|
|
442
446
|
"run_errors_log": run_logs / f"errors-{task_type_segment}-{sequences['state']}.jsonl",
|
|
443
447
|
"claude_worker_errors_sidecar": worker_results / f"claude-worker-errors{suffixes['worker_results']}.json",
|
|
444
448
|
"codex_worker_errors_sidecar": worker_results / f"codex-worker-errors{suffixes['worker_results']}.json",
|
|
@@ -570,6 +574,10 @@ def _absolute_worker_fields(paths: Mapping[str, Any]) -> dict[str, str]:
|
|
|
570
574
|
"CODEX_WORKER_RESULT_FILE": str(paths["codex_worker_result"]),
|
|
571
575
|
"ANTIGRAVITY_WORKER_RESULT_FILE": str(paths["antigravity_worker_result"]),
|
|
572
576
|
"REPORT_WRITER_WORKER_RESULT_FILE": str(paths["report_writer_worker_result"]),
|
|
577
|
+
"REPORT_WRITER_NARRATIVE_FILE": str(paths["report_writer_narrative"]),
|
|
578
|
+
"APPROVAL_DECISIONS_PATH": str(paths["approval_decisions"]),
|
|
579
|
+
"DESIGN_PREPARATION_PATH": str(paths["design_preparation"]),
|
|
580
|
+
"PLAN_BODY_VERIFICATION_STATE_PATH": str(paths["plan_body_verification"]),
|
|
573
581
|
"CLAUDE_WORKER_ERRORS_SIDECAR_PATH": str(paths["claude_worker_errors_sidecar"]),
|
|
574
582
|
"CODEX_WORKER_ERRORS_SIDECAR_PATH": str(paths["codex_worker_errors_sidecar"]),
|
|
575
583
|
"ANTIGRAVITY_WORKER_ERRORS_SIDECAR_PATH": str(paths["antigravity_worker_errors_sidecar"]),
|
|
@@ -670,6 +678,10 @@ def _relative_file_fields(project_root: Path, paths: Mapping[str, Any]) -> dict[
|
|
|
670
678
|
"CODEX_WORKER_RESULT_RELATIVE_PATH": _rel(project_root, paths["codex_worker_result"]),
|
|
671
679
|
"ANTIGRAVITY_WORKER_RESULT_RELATIVE_PATH": _rel(project_root, paths["antigravity_worker_result"]),
|
|
672
680
|
"REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH": _rel(project_root, paths["report_writer_worker_result"]),
|
|
681
|
+
"REPORT_WRITER_NARRATIVE_RELATIVE_PATH": _rel(project_root, paths["report_writer_narrative"]),
|
|
682
|
+
"APPROVAL_DECISIONS_RELATIVE_PATH": _rel(project_root, paths["approval_decisions"]),
|
|
683
|
+
"DESIGN_PREPARATION_RELATIVE_PATH": _rel(project_root, paths["design_preparation"]),
|
|
684
|
+
"PLAN_BODY_VERIFICATION_STATE_RELATIVE_PATH": _rel(project_root, paths["plan_body_verification"]),
|
|
673
685
|
"RUN_ERRORS_LOG_RELATIVE_PATH": _rel(project_root, paths["run_errors_log"]),
|
|
674
686
|
"CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH": _rel(project_root, paths["claude_worker_errors_sidecar"]),
|
|
675
687
|
"CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH": _rel(project_root, paths["codex_worker_errors_sidecar"]),
|
|
@@ -552,6 +552,9 @@ def compute_run_paths(
|
|
|
552
552
|
team_state = run_state / f"team-state{suffixes['state']}.json"
|
|
553
553
|
active_run_context = run_state / f"active-run-context{suffixes['state']}.json"
|
|
554
554
|
lead_events = run_state / f"lead-events-{task_type_segment}-{seqs['state']}.jsonl"
|
|
555
|
+
convergence_state = (
|
|
556
|
+
run_state / f"convergence-{task_type_segment}-{seqs['state']}.json"
|
|
557
|
+
)
|
|
555
558
|
final_report_template = instruction_set / "final-report-template.md"
|
|
556
559
|
final_report_schema = instruction_set / "final-report-schema.json"
|
|
557
560
|
reference_expectations = instruction_set / "reference-expectations.md"
|
|
@@ -562,6 +565,10 @@ def compute_run_paths(
|
|
|
562
565
|
codex_worker_result = worker_results / f"codex-worker{suffixes['worker_results']}.md"
|
|
563
566
|
antigravity_worker_result = worker_results / f"antigravity-worker{suffixes['worker_results']}.md"
|
|
564
567
|
report_writer_worker_result = worker_results / f"report-writer-worker{suffixes['worker_results']}.md"
|
|
568
|
+
report_writer_narrative = worker_results / f"report-writer-narrative{suffixes['worker_results']}.md"
|
|
569
|
+
approval_decisions = run_state / f"approval-decisions-{task_type_segment}-{seqs['state']}.json"
|
|
570
|
+
design_preparation = run_state / f"design-preparation-{task_type_segment}-{seqs['state']}.json"
|
|
571
|
+
plan_body_verification = run_state / f"plan-body-verification-{task_type_segment}-{seqs['state']}.json"
|
|
565
572
|
|
|
566
573
|
run_errors_log = run_logs / f"errors-{task_type_segment}-{seqs['state']}.jsonl"
|
|
567
574
|
runtime_home = okstra_home()
|
|
@@ -667,6 +674,11 @@ def compute_run_paths(
|
|
|
667
674
|
"CODEX_WORKER_RESULT_FILE": str(codex_worker_result),
|
|
668
675
|
"ANTIGRAVITY_WORKER_RESULT_FILE": str(antigravity_worker_result),
|
|
669
676
|
"REPORT_WRITER_WORKER_RESULT_FILE": str(report_writer_worker_result),
|
|
677
|
+
"REPORT_WRITER_NARRATIVE_FILE": str(report_writer_narrative),
|
|
678
|
+
"APPROVAL_DECISIONS_PATH": str(approval_decisions),
|
|
679
|
+
"DESIGN_PREPARATION_PATH": str(design_preparation),
|
|
680
|
+
"CONVERGENCE_STATE_PATH": str(convergence_state),
|
|
681
|
+
"PLAN_BODY_VERIFICATION_STATE_PATH": str(plan_body_verification),
|
|
670
682
|
"RUN_ERRORS_LOG_PATH": str(run_errors_log),
|
|
671
683
|
"CLAUDE_WORKER_ERRORS_SIDECAR_PATH": str(claude_worker_errors_sidecar),
|
|
672
684
|
"CODEX_WORKER_ERRORS_SIDECAR_PATH": str(codex_worker_errors_sidecar),
|
|
@@ -724,6 +736,11 @@ def compute_run_paths(
|
|
|
724
736
|
("FINAL_REPORT_RECORD_RELATIVE_PATH", final_report),
|
|
725
737
|
("FINAL_STATUS_RELATIVE_PATH", final_status),
|
|
726
738
|
("TEAM_STATE_RELATIVE_PATH", team_state),
|
|
739
|
+
# `render.py` 가 이 키로 매니페스트의 `convergenceStatePath` 를 채운다.
|
|
740
|
+
# `path_hints` 만 내보내던 동안 그 필드가 빈 문자열로 렌더돼,
|
|
741
|
+
# `report-finalize` 가 "report contract 3.0 requires convergenceStatePath"
|
|
742
|
+
# 로 죽었다 — 수렴 상태 파일 자체는 정상 생성돼 있었다.
|
|
743
|
+
("CONVERGENCE_STATE_RELATIVE_PATH", convergence_state),
|
|
727
744
|
("ACTIVE_RUN_CONTEXT_RELATIVE_PATH", active_run_context),
|
|
728
745
|
("LEAD_EVENTS_RELATIVE_PATH", lead_events),
|
|
729
746
|
("WORKER_RESULTS_RELATIVE_PATH", worker_results),
|
|
@@ -737,6 +754,10 @@ def compute_run_paths(
|
|
|
737
754
|
("CODEX_WORKER_RESULT_RELATIVE_PATH", codex_worker_result),
|
|
738
755
|
("ANTIGRAVITY_WORKER_RESULT_RELATIVE_PATH", antigravity_worker_result),
|
|
739
756
|
("REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH", report_writer_worker_result),
|
|
757
|
+
("REPORT_WRITER_NARRATIVE_RELATIVE_PATH", report_writer_narrative),
|
|
758
|
+
("APPROVAL_DECISIONS_RELATIVE_PATH", approval_decisions),
|
|
759
|
+
("DESIGN_PREPARATION_RELATIVE_PATH", design_preparation),
|
|
760
|
+
("PLAN_BODY_VERIFICATION_STATE_RELATIVE_PATH", plan_body_verification),
|
|
740
761
|
("RUN_ERRORS_LOG_RELATIVE_PATH", run_errors_log),
|
|
741
762
|
("CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", claude_worker_errors_sidecar),
|
|
742
763
|
("CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", codex_worker_errors_sidecar),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Deterministic extraction of implementation-planning verification items."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import re
|
|
4
5
|
from collections.abc import Mapping
|
|
5
6
|
from copy import deepcopy
|
|
6
7
|
from typing import Any
|
|
@@ -50,6 +51,87 @@ def _non_empty_string(value: object) -> str | None:
|
|
|
50
51
|
return None
|
|
51
52
|
|
|
52
53
|
|
|
54
|
+
# 자연키가 있는 소스. 이 필드는 요소의 *정체*이지 내용이 아니다 — 명령이나 문구를 고쳐도
|
|
55
|
+
# 같은 요소로 남고, 앞의 형제가 지워져도 값이 변하지 않는다.
|
|
56
|
+
_NATURAL_KEY_FIELDS: dict[str, tuple[str, ...]] = {
|
|
57
|
+
"optionCandidates": ("name",),
|
|
58
|
+
"dependencyMigrationRisk": ("id",),
|
|
59
|
+
"validationChecklist": ("id",),
|
|
60
|
+
"rollbackStrategy": ("id",),
|
|
61
|
+
"requirementCoverage": ("id", "originalRequirementId"),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _element_key(value: str) -> str:
|
|
66
|
+
return re.sub(r"[^a-z0-9]+", "-", value.strip().lower()).strip("-")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _element_id(kind: str, key: str, fallback: str) -> str:
|
|
70
|
+
"""이 요소의 안정 식별자. 자연키가 없으면 위치 id 로 떨어진다.
|
|
71
|
+
|
|
72
|
+
`P-Opt-N` / `P-Val-N` / `P-Req-N` 같은 위치 id 는 서수라, 자기수정이 요소
|
|
73
|
+
하나를 지우면 뒤가 전부 밀린다 — 재작성 전의 `P-Req-16` 과 후의 `P-Req-16` 이
|
|
74
|
+
같은 요소라는 보장이 없다(`prompts/lead/plan-body-verification.md` §7). 그래서
|
|
75
|
+
라운드나 run 을 가로질러 판정을 이월할 수 없고, 검증 표면이 매번 전체로
|
|
76
|
+
돌아간다.
|
|
77
|
+
|
|
78
|
+
`elementId` 는 그 대조를 가능하게 한다. 위치가 아니라 요소가 스스로 지닌
|
|
79
|
+
이름(`R-001`, `VC-011`, 스테이지·스텝 좌표)에서 만들어지므로 형제의 삭제에
|
|
80
|
+
영향받지 않는다. 표시와 인용은 계속 `id` 를 쓴다 — 이 값은 대조용이다.
|
|
81
|
+
|
|
82
|
+
자연키가 없는 소스는 위치 id 로 떨어지고, 그 항목은 삭제를 가로질러 안정하지
|
|
83
|
+
않다. 소비자는 `elementId == id` 인 항목을 그렇게 다뤄야 한다.
|
|
84
|
+
"""
|
|
85
|
+
normalized = _element_key(key)
|
|
86
|
+
return f"{kind}:{normalized}" if normalized else fallback
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# 자기 기록 블록. 계획은 실행 지시와 그 실행의 감사 기록을 한 산출물에 담는데,
|
|
90
|
+
# 게이트가 둘을 구분하지 않으면 기록의 부정확이 구현 착수를 막는다 — 실측 run 에서
|
|
91
|
+
# 남은 차단 4건이 전부 "사용자가 내린 결정을 계획이 정확히 반영했는가" 였다.
|
|
92
|
+
#
|
|
93
|
+
# 커버리지 행이 유일한 기록 항목이다. 승인 처분과 decision 참조는 그 행의 필드이고,
|
|
94
|
+
# 나머지 소스는 전부 무엇을 어떻게 만드는지를 말한다.
|
|
95
|
+
_RECORD_SOURCES = frozenset({"requirementCoverage"})
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _item_block(source: str) -> str:
|
|
99
|
+
return "record" if source in _RECORD_SOURCES else "execution"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _stage_scope(row: Mapping[str, Any] | None, *coordinates: int) -> list[int] | None:
|
|
103
|
+
"""이 항목이 걸리는 스테이지. 어디에도 안 걸리면 ``None``.
|
|
104
|
+
|
|
105
|
+
게이트 범위를 다음 실행 스테이지로 좁히려면 항목마다 이 값이 있어야 한다.
|
|
106
|
+
스텝과 design-prep 은 id 자체가 좌표를 지니지만, 커버리지 행과 검증 행은
|
|
107
|
+
`stageRefs` 를 실어야 알 수 있다 — 그 전에는 `coveredBy` 산문을 정규식으로
|
|
108
|
+
읽는 수밖에 없었고, 그것은 게이트 판정의 근거가 되지 못한다.
|
|
109
|
+
|
|
110
|
+
``None`` 은 "스테이지에 걸리지 않는다" 이고, 소비처는 이를 **모든 스테이지에
|
|
111
|
+
적용** 으로 읽는다. 그게 안전한 쪽이고 지금 동작과 같다. `P-Opt-*` 처럼 계획
|
|
112
|
+
전체의 성격을 판정하는 항목이 여기 해당하고, `stageRefs` 를 아직 안 쓰는 계획도
|
|
113
|
+
같은 자리로 떨어진다.
|
|
114
|
+
"""
|
|
115
|
+
if coordinates:
|
|
116
|
+
return [int(value) for value in coordinates]
|
|
117
|
+
refs = (row or {}).get("stageRefs")
|
|
118
|
+
if not isinstance(refs, list):
|
|
119
|
+
return None
|
|
120
|
+
stages = [
|
|
121
|
+
value for value in refs
|
|
122
|
+
if isinstance(value, int) and not isinstance(value, bool) and value >= 1
|
|
123
|
+
]
|
|
124
|
+
return stages or None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _natural_element_id(source: str, prefix: str, row: Mapping[str, Any], item_id: str) -> str:
|
|
128
|
+
for field in _NATURAL_KEY_FIELDS.get(source, ()):
|
|
129
|
+
value = _non_empty_string(row.get(field))
|
|
130
|
+
if value is not None:
|
|
131
|
+
return _element_id(prefix.removeprefix("P-").lower(), value, item_id)
|
|
132
|
+
return item_id
|
|
133
|
+
|
|
134
|
+
|
|
53
135
|
def _subject(row: Mapping[str, Any], source: str) -> str:
|
|
54
136
|
for field in _SUBJECT_FIELDS[source]:
|
|
55
137
|
value = _non_empty_string(row.get(field))
|
|
@@ -77,6 +159,11 @@ def _row_payload(row: Mapping[str, Any], **coordinates: int) -> dict[str, Any]:
|
|
|
77
159
|
def _add_item(items: list[dict[str, Any]], item: dict[str, Any]) -> None:
|
|
78
160
|
if any(existing["id"] == item["id"] for existing in items):
|
|
79
161
|
raise PlanItemContractError(f"duplicate plan item ID: {item['id']}")
|
|
162
|
+
element_id = item.get("elementId")
|
|
163
|
+
if element_id and any(existing.get("elementId") == element_id for existing in items):
|
|
164
|
+
# 두 요소가 같은 자연키를 쓰면 이월이 엉뚱한 항목에 붙는다. 위치 id 가
|
|
165
|
+
# 갈라져 있어도 대조는 elementId 로 하므로 여기서 막는다.
|
|
166
|
+
raise PlanItemContractError(f"duplicate plan element ID: {element_id}")
|
|
80
167
|
items.append(item)
|
|
81
168
|
|
|
82
169
|
|
|
@@ -115,6 +202,9 @@ def _extract_standard_items(planning: Mapping[str, Any]) -> list[dict[str, Any]]
|
|
|
115
202
|
items,
|
|
116
203
|
{
|
|
117
204
|
"id": "P-Dir-1",
|
|
205
|
+
"elementId": "dir:selected-direction",
|
|
206
|
+
"stageScope": None,
|
|
207
|
+
"block": "execution",
|
|
118
208
|
"subject": _non_empty_string(realization.get("goal"))
|
|
119
209
|
or "Selected direction realization",
|
|
120
210
|
"sourceSection": "Selected Direction",
|
|
@@ -127,10 +217,14 @@ def _extract_standard_items(planning: Mapping[str, Any]) -> list[dict[str, Any]]
|
|
|
127
217
|
_extract_step_items(planning, items, prefix, section)
|
|
128
218
|
continue
|
|
129
219
|
for index, row in enumerate(_rows(planning, source), start=1):
|
|
220
|
+
item_id = f"{prefix}-{index}"
|
|
130
221
|
_add_item(
|
|
131
222
|
items,
|
|
132
223
|
{
|
|
133
|
-
"id":
|
|
224
|
+
"id": item_id,
|
|
225
|
+
"elementId": _natural_element_id(source, prefix, row, item_id),
|
|
226
|
+
"stageScope": _stage_scope(row),
|
|
227
|
+
"block": _item_block(source),
|
|
134
228
|
"subject": _subject(row, source),
|
|
135
229
|
"sourceSection": section,
|
|
136
230
|
"ticketId": _ticket_id(row),
|
|
@@ -151,8 +245,11 @@ def _extract_step_items(
|
|
|
151
245
|
_add_item(
|
|
152
246
|
items,
|
|
153
247
|
{
|
|
248
|
+
# 평면 배열에는 좌표가 없어 위치 id 로 떨어진다.
|
|
154
249
|
"id": f"{prefix}-{index}",
|
|
155
|
-
"
|
|
250
|
+
"elementId": f"{prefix}-{index}",
|
|
251
|
+
"stageScope": _stage_scope(row),
|
|
252
|
+
"block": "execution", "subject": _subject(row, "stepwiseExecution"),
|
|
156
253
|
"sourceSection": section,
|
|
157
254
|
"ticketId": _ticket_id(row),
|
|
158
255
|
"payload": _row_payload(row),
|
|
@@ -172,6 +269,9 @@ def _extract_step_items(
|
|
|
172
269
|
items,
|
|
173
270
|
{
|
|
174
271
|
"id": f"{prefix}-{stage_number}.{step_number}",
|
|
272
|
+
"elementId": f"step:{stage_number}.{step_number}",
|
|
273
|
+
"stageScope": _stage_scope(None, stage_number),
|
|
274
|
+
"block": "execution",
|
|
175
275
|
"subject": _subject(row, "stepwiseExecution"),
|
|
176
276
|
"sourceSection": section,
|
|
177
277
|
"ticketId": _ticket_id(row),
|
|
@@ -212,6 +312,9 @@ def _extract_prep_items(
|
|
|
212
312
|
items,
|
|
213
313
|
{
|
|
214
314
|
"id": item_id,
|
|
315
|
+
"elementId": f"prep:{trigger.stage}:{_element_key(trigger.kind)}",
|
|
316
|
+
"stageScope": _stage_scope(None, trigger.stage),
|
|
317
|
+
"block": "execution",
|
|
215
318
|
"subject": f"Stage {trigger.stage} {trigger.kind} design preparation",
|
|
216
319
|
"sourceSection": "5.5.10",
|
|
217
320
|
"ticketId": "",
|
|
@@ -243,7 +346,9 @@ def _extract_variation_point_items(
|
|
|
243
346
|
items,
|
|
244
347
|
{
|
|
245
348
|
"id": "P-Var-0",
|
|
246
|
-
"
|
|
349
|
+
"elementId": "var:none",
|
|
350
|
+
"stageScope": None,
|
|
351
|
+
"block": "execution", "subject": "No variation point declared",
|
|
247
352
|
"sourceSection": "5.5.11",
|
|
248
353
|
"ticketId": "",
|
|
249
354
|
"payload": _row_payload(dict(analysis)),
|
|
@@ -259,7 +364,13 @@ def _extract_variation_point_items(
|
|
|
259
364
|
items,
|
|
260
365
|
{
|
|
261
366
|
"id": f"P-Var-{index}",
|
|
262
|
-
"
|
|
367
|
+
"elementId": _element_id(
|
|
368
|
+
"var",
|
|
369
|
+
_non_empty_string(point.get("behavior")) or "",
|
|
370
|
+
f"P-Var-{index}",
|
|
371
|
+
),
|
|
372
|
+
"stageScope": None,
|
|
373
|
+
"block": "execution", "subject": _non_empty_string(point.get("behavior"))
|
|
263
374
|
or f"variation point {index}",
|
|
264
375
|
"sourceSection": "5.5.11",
|
|
265
376
|
"ticketId": "",
|