okstra 0.186.7 → 0.187.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 +1 -1
- package/dist/cli-registry.d.mts +88 -1
- package/dist/cli-registry.mjs +68 -111
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +0 -1
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +8 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.mjs +1 -14
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/config.mjs +38 -40
- package/dist/commands/lifecycle/config.mjs.map +1 -1
- package/dist/commands/lifecycle/doctor.d.mts +22 -7
- package/dist/commands/lifecycle/doctor.mjs +77 -49
- package/dist/commands/lifecycle/doctor.mjs.map +1 -1
- package/dist/commands/lifecycle/install.d.mts +12 -10
- package/dist/commands/lifecycle/install.mjs +104 -39
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/lifecycle/paths.mjs +8 -3
- package/dist/commands/lifecycle/paths.mjs.map +1 -1
- package/dist/commands/lifecycle/preflight.mjs +2 -1
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/lifecycle/setup.mjs +22 -36
- package/dist/commands/lifecycle/setup.mjs.map +1 -1
- package/dist/commands/lifecycle/uninstall.d.mts +4 -2
- package/dist/commands/lifecycle/uninstall.mjs +59 -15
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
- package/dist/commands/memory/memory.mjs +7 -1
- package/dist/commands/memory/memory.mjs.map +1 -1
- package/dist/lib/helper-scripts.d.mts +1 -1
- package/dist/lib/helper-scripts.mjs +10 -18
- package/dist/lib/helper-scripts.mjs.map +1 -1
- package/dist/lib/host-config.d.mts +72 -0
- package/dist/lib/host-config.mjs +404 -0
- package/dist/lib/host-config.mjs.map +1 -0
- package/dist/lib/host-registry-client.d.mts +2 -2
- package/dist/lib/host-registry-client.mjs +0 -3
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/dist/lib/install-assets.d.mts +1 -0
- package/dist/lib/install-assets.mjs +4 -0
- package/dist/lib/install-assets.mjs.map +1 -1
- package/dist/lib/proc.d.mts +2 -0
- package/dist/lib/proc.mjs +12 -0
- package/dist/lib/proc.mjs.map +1 -1
- package/dist/lib/python-command.d.mts +2 -0
- package/dist/lib/python-command.mjs +52 -0
- package/dist/lib/python-command.mjs.map +1 -0
- package/dist/lib/python-helper.d.mts +2 -5
- package/dist/lib/python-helper.mjs +3 -52
- package/dist/lib/python-helper.mjs.map +1 -1
- package/dist/lib/runtime-payload.d.mts +23 -0
- package/dist/lib/runtime-payload.mjs +57 -0
- package/dist/lib/runtime-payload.mjs.map +1 -0
- package/dist/lib/types.d.mts +24 -13
- package/docs/architecture/storage-model.md +21 -5
- package/docs/architecture.md +37 -36
- package/docs/cli.md +55 -76
- package/docs/coding-rules.md +295 -0
- package/docs/container.md +10 -36
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/skills/okstra-code-review.md +0 -1
- package/docs/for-ai/skills/okstra-container-build.md +8 -41
- package/docs/for-ai/skills/okstra-inspect.md +4 -4
- package/docs/for-ai/skills/okstra-manager.md +2 -2
- package/docs/for-ai/skills/okstra-rollup.md +0 -1
- package/docs/for-ai/skills/okstra-run.md +3 -3
- package/docs/for-ai/skills/okstra-user-response.md +0 -1
- package/docs/performance-improvement-plan-v2.md +1 -1
- package/docs/project-structure-overview.md +62 -62
- package/docs/task-process/README.md +3 -3
- package/docs/task-process/common-flow.md +1 -1
- package/docs/task-process/error-analysis.md +3 -3
- package/docs/task-process/implementation-planning.md +3 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +6 -11
- package/runtime/agents/workers/report-writer-worker.md +1 -1
- package/runtime/bin/lib/okstra/cli.sh +4 -0
- package/runtime/bin/lib/okstra/globals.sh +2 -0
- package/runtime/bin/lib/okstra/interactive.sh +26 -173
- package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
- package/runtime/bin/lib/okstra/usage.sh +3 -3
- package/runtime/bin/okstra-compact-reminder.sh +1 -5
- package/runtime/bin/okstra-error-log.py +19 -2
- package/runtime/bin/okstra-import-check.py +26 -0
- package/runtime/bin/okstra-inject-report-index.py +5 -4
- package/runtime/bin/okstra-provider-exec.py +22 -17
- package/runtime/bin/okstra-render-final-report.py +16 -6
- package/runtime/bin/okstra-render-report-views.py +25 -18
- package/runtime/bin/okstra-report-translate.py +30 -18
- package/runtime/bin/okstra-spawn-followups.py +4 -0
- package/runtime/bin/okstra-token-usage.py +3 -0
- package/runtime/bin/okstra.sh +4 -2
- package/runtime/bin/okstra_bootstrap.py +58 -0
- package/runtime/prompts/coding-preflight/overview.md +1 -1
- package/runtime/prompts/duties/planning-worker.md +1 -1
- package/runtime/prompts/launch.template.md +35 -9
- package/runtime/prompts/lead/convergence.md +96 -87
- package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
- package/runtime/prompts/lead/plan-body-verification.md +65 -46
- package/runtime/prompts/lead/report-writer.md +34 -4
- package/runtime/prompts/lead/team-contract.md +4 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +3 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
- package/runtime/prompts/profiles/error-analysis.md +4 -3
- package/runtime/prompts/profiles/final-verification.md +2 -2
- package/runtime/prompts/profiles/implementation-planning.md +22 -17
- package/runtime/prompts/profiles/implementation.md +3 -2
- package/runtime/prompts/profiles/requirements-discovery.md +2 -1
- package/runtime/prompts/wizard/prompts.ko.json +29 -8
- package/runtime/python/okstra_ctl/__init__.py +6 -27
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +40 -22
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +14 -21
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
- package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
- package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
- package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +12 -1
- package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
- package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +215 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +408 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
- package/runtime/python/okstra_ctl/approval_decisions.py +260 -8
- package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
- package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
- package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
- package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
- package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
- package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
- package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
- package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
- package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
- package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
- package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
- package/runtime/python/okstra_ctl/cmux.py +38 -31
- package/runtime/python/okstra_ctl/code_review_target.py +163 -1
- package/runtime/python/okstra_ctl/conformance.py +23 -0
- package/runtime/python/okstra_ctl/container.py +38 -421
- package/runtime/python/okstra_ctl/context_cost.py +16 -2
- package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
- package/runtime/python/okstra_ctl/convergence.py +101 -15
- package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
- package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
- package/runtime/python/okstra_ctl/convergence_store.py +5 -29
- package/runtime/python/okstra_ctl/design_prep.py +108 -12
- package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
- package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
- package/runtime/python/okstra_ctl/dispatch_core.py +102 -25
- package/runtime/python/okstra_ctl/dispatch_state.py +268 -39
- package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -1
- package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
- package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
- package/runtime/python/okstra_ctl/error_log_core.py +1 -1
- package/runtime/python/okstra_ctl/error_report.py +14 -0
- package/runtime/python/okstra_ctl/error_zip.py +18 -2
- package/runtime/python/okstra_ctl/execution_identity.py +100 -7
- package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
- package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +90 -16
- package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
- package/runtime/python/okstra_ctl/handoff.py +24 -1
- package/runtime/python/okstra_ctl/ids.py +6 -16
- package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
- package/runtime/python/okstra_ctl/implementation_options.py +25 -11
- package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
- package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
- package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
- package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
- package/runtime/python/okstra_ctl/index.py +2 -2
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
- package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
- package/runtime/python/okstra_ctl/json_boundary.py +10 -0
- package/runtime/python/okstra_ctl/listing.py +0 -91
- package/runtime/python/okstra_ctl/locks.py +5 -19
- package/runtime/python/okstra_ctl/log_report.py +14 -0
- package/runtime/python/okstra_ctl/manager_cli.py +22 -3
- package/runtime/python/okstra_ctl/manager_launch.py +4 -8
- package/runtime/python/okstra_ctl/material.py +2 -2
- package/runtime/python/okstra_ctl/migrate.py +23 -1
- package/runtime/python/okstra_ctl/model_cli.py +19 -5
- package/runtime/python/okstra_ctl/model_discovery.py +46 -47
- package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
- package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
- package/runtime/python/okstra_ctl/model_io/references.py +370 -0
- package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
- package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
- package/runtime/python/okstra_ctl/model_pool.py +10 -6
- package/runtime/python/okstra_ctl/models.py +8 -8
- package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
- package/runtime/python/okstra_ctl/next_phase.py +121 -69
- package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
- package/runtime/python/okstra_ctl/pane_title.py +11 -8
- package/runtime/python/okstra_ctl/path_hints.py +36 -11
- package/runtime/python/okstra_ctl/paths.py +76 -8
- package/runtime/python/okstra_ctl/plan_items.py +132 -16
- package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
- package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
- package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
- package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
- package/runtime/python/okstra_ctl/prepare_error.py +20 -0
- package/runtime/python/okstra_ctl/prior_planning.py +157 -0
- package/runtime/python/okstra_ctl/profile_show.py +18 -1
- package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
- package/runtime/python/okstra_ctl/recap.py +22 -1
- package/runtime/python/okstra_ctl/reconcile.py +84 -0
- package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
- package/runtime/python/okstra_ctl/render.py +98 -32
- package/runtime/python/okstra_ctl/render_final_report.py +3 -7
- package/runtime/python/okstra_ctl/report_assembly.py +297 -7
- package/runtime/python/okstra_ctl/report_contract.py +30 -7
- package/runtime/python/okstra_ctl/report_finalize.py +206 -15
- package/runtime/python/okstra_ctl/report_html/common.py +2 -2
- package/runtime/python/okstra_ctl/report_html/render.py +8 -12
- package/runtime/python/okstra_ctl/report_language.py +8 -4
- package/runtime/python/okstra_ctl/report_narrative.py +90 -3
- package/runtime/python/okstra_ctl/report_projections.py +8 -2
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
- package/runtime/python/okstra_ctl/report_translation.py +2 -35
- package/runtime/python/okstra_ctl/report_views.py +4 -22
- package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
- package/runtime/python/okstra_ctl/rollup.py +13 -0
- package/runtime/python/okstra_ctl/run.py +639 -138
- package/runtime/python/okstra_ctl/run_audit.py +13 -0
- package/runtime/python/okstra_ctl/run_index_row.py +0 -12
- package/runtime/python/okstra_ctl/seeding.py +21 -11
- package/runtime/python/okstra_ctl/sequence.py +1 -1
- package/runtime/python/okstra_ctl/session.py +64 -14
- package/runtime/python/okstra_ctl/set_work_status.py +18 -0
- package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
- package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
- package/runtime/python/okstra_ctl/stage_map.py +57 -11
- package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
- package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
- package/runtime/python/okstra_ctl/stage_targets.py +3 -12
- package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
- package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
- package/runtime/python/okstra_ctl/task_target.py +4 -16
- package/runtime/python/okstra_ctl/team.py +49 -2
- package/runtime/python/okstra_ctl/time_report.py +17 -2
- package/runtime/python/okstra_ctl/usage_report.py +11 -0
- package/runtime/python/okstra_ctl/user_response.py +55 -218
- package/runtime/python/okstra_ctl/user_response_values.py +242 -0
- package/runtime/python/okstra_ctl/validation_contract.py +3 -0
- package/runtime/python/okstra_ctl/verification_target.py +68 -0
- package/runtime/python/okstra_ctl/wizard.py +457 -92
- package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
- package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
- package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
- package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
- package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
- package/runtime/python/okstra_ctl/worker_request.py +2 -1
- package/runtime/python/okstra_ctl/worker_runner.py +16 -9
- package/runtime/python/okstra_ctl/worker_state.py +16 -1
- package/runtime/python/okstra_ctl/workflow.py +18 -1
- package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
- package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
- package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
- package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
- package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
- package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
- package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
- package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
- package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
- package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
- package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
- package/runtime/python/okstra_ctl/write_policy.py +90 -213
- package/runtime/python/okstra_project/__init__.py +0 -4
- package/runtime/python/okstra_project/dirs.py +2 -2
- package/runtime/python/okstra_project/phase_pointer.py +84 -0
- package/runtime/python/okstra_project/slug.py +24 -0
- package/runtime/python/okstra_project/state.py +22 -230
- package/runtime/python/okstra_token_usage/claude.py +9 -1
- package/runtime/python/okstra_token_usage/cli.py +3 -1
- package/runtime/python/okstra_token_usage/collect.py +71 -10
- package/runtime/python/okstra_token_usage/cursor.py +2 -0
- package/runtime/python/okstra_token_usage/grok.py +1 -5
- package/runtime/python/okstra_token_usage/paths.py +26 -0
- package/runtime/python/okstra_token_usage/pricing.py +19 -7
- package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
- package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
- package/runtime/schemas/final-report-v2.0.schema.json +9 -26
- package/runtime/schemas/final-report-v3.0.schema.json +173 -41
- package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
- package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
- package/runtime/skills/okstra-container-build/SKILL.md +16 -47
- package/runtime/skills/okstra-inspect/facets/history.md +1 -1
- package/runtime/skills/okstra-inspect/facets/status.md +7 -6
- package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +7 -5
- package/runtime/templates/report-writer-prompt-preamble.md +1 -1
- package/runtime/templates/reports/brief.template.md +6 -2
- package/runtime/templates/reports/error-analysis-input.template.md +2 -0
- package/runtime/templates/reports/final-verification-input.template.md +2 -0
- package/runtime/templates/reports/implementation-input.template.md +7 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
- package/runtime/templates/reports/quick-input.template.md +2 -0
- package/runtime/templates/reports/release-handoff-input.template.md +6 -1
- package/runtime/templates/reports/report.js +20 -3
- package/runtime/templates/reports/schedule.template.md +2 -0
- package/runtime/templates/reports/settings.template.json +0 -10
- package/runtime/templates/reports/task-brief.template.md +3 -0
- package/runtime/templates/reports/user-response.template.md +7 -3
- package/runtime/validators/checks/fixtures-01.py +57 -0
- package/runtime/validators/checks/fixtures-02.py +565 -0
- package/runtime/validators/checks/runners-01.py +108 -0
- package/runtime/validators/checks/validate-assets-01.py +60 -0
- package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
- package/runtime/validators/checks/validate-tasks-01.py +46 -0
- package/runtime/validators/checks/validate-tasks-02.py +85 -0
- package/runtime/validators/checks/validate-tasks-03.py +61 -0
- package/runtime/validators/checks/validate-tasks-04.py +118 -0
- package/runtime/validators/forbidden_actions.py +73 -3
- package/runtime/validators/lib/common.sh +5 -0
- package/runtime/validators/lib/fixtures.sh +7 -591
- package/runtime/validators/lib/paths.sh +13 -0
- package/runtime/validators/lib/runners.sh +6 -104
- package/runtime/validators/lib/summary.sh +1 -1
- package/runtime/validators/lib/validate-assets.sh +6 -56
- package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
- package/runtime/validators/lib/validate-tasks.sh +9 -294
- package/runtime/validators/validate-implementation-plan-stages.py +4 -4
- package/runtime/validators/validate-run.py +1369 -2654
- package/runtime/validators/validate-workflow.sh +56 -16
- package/runtime/validators/validate_improvement_report.py +2 -1
- package/runtime/validators/validate_session_conformance.py +295 -49
- package/dist/commands/execute/agent-prompt.d.mts +0 -1
- package/dist/commands/execute/agent-prompt.mjs +0 -24
- package/dist/commands/execute/agent-prompt.mjs.map +0 -1
- package/dist/commands/execute/codex-dispatch.d.mts +0 -3
- package/dist/commands/execute/codex-dispatch.mjs +0 -6
- package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
- package/dist/commands/execute/codex-run.d.mts +0 -3
- package/dist/commands/execute/codex-run.mjs +0 -62
- package/dist/commands/execute/codex-run.mjs.map +0 -1
- package/dist/commands/execute/convergence.d.mts +0 -1
- package/dist/commands/execute/convergence.mjs +0 -38
- package/dist/commands/execute/convergence.mjs.map +0 -1
- package/dist/commands/execute/error-log.d.mts +0 -1
- package/dist/commands/execute/error-log.mjs +0 -18
- package/dist/commands/execute/error-log.mjs.map +0 -1
- package/dist/commands/execute/git-reconcile.d.mts +0 -1
- package/dist/commands/execute/git-reconcile.mjs +0 -30
- package/dist/commands/execute/git-reconcile.mjs.map +0 -1
- package/dist/commands/execute/handoff.d.mts +0 -1
- package/dist/commands/execute/handoff.mjs +0 -31
- package/dist/commands/execute/handoff.mjs.map +0 -1
- package/dist/commands/execute/incremental-carry.d.mts +0 -1
- package/dist/commands/execute/incremental-carry.mjs +0 -20
- package/dist/commands/execute/incremental-carry.mjs.map +0 -1
- package/dist/commands/execute/incremental-scope.d.mts +0 -1
- package/dist/commands/execute/incremental-scope.mjs +0 -29
- package/dist/commands/execute/incremental-scope.mjs.map +0 -1
- package/dist/commands/execute/integrate-stages.d.mts +0 -1
- package/dist/commands/execute/integrate-stages.mjs +0 -24
- package/dist/commands/execute/integrate-stages.mjs.map +0 -1
- package/dist/commands/execute/pane-title.d.mts +0 -1
- package/dist/commands/execute/pane-title.mjs +0 -20
- package/dist/commands/execute/pane-title.mjs.map +0 -1
- package/dist/commands/execute/plan-items.d.mts +0 -1
- package/dist/commands/execute/plan-items.mjs +0 -9
- package/dist/commands/execute/plan-items.mjs.map +0 -1
- package/dist/commands/execute/plan-validate.d.mts +0 -1
- package/dist/commands/execute/plan-validate.mjs +0 -68
- package/dist/commands/execute/plan-validate.mjs.map +0 -1
- package/dist/commands/execute/plan-verify.d.mts +0 -1
- package/dist/commands/execute/plan-verify.mjs +0 -43
- package/dist/commands/execute/plan-verify.mjs.map +0 -1
- package/dist/commands/execute/spawn-followups.d.mts +0 -1
- package/dist/commands/execute/spawn-followups.mjs +0 -22
- package/dist/commands/execute/spawn-followups.mjs.map +0 -1
- package/dist/commands/execute/team.d.mts +0 -3
- package/dist/commands/execute/team.mjs +0 -66
- package/dist/commands/execute/team.mjs.map +0 -1
- package/dist/commands/execute/token-usage.d.mts +0 -1
- package/dist/commands/execute/token-usage.mjs +0 -19
- package/dist/commands/execute/token-usage.mjs.map +0 -1
- package/dist/commands/execute/worker-audit-check.d.mts +0 -1
- package/dist/commands/execute/worker-audit-check.mjs +0 -34
- package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
- package/dist/commands/execute/worker-dispatch.d.mts +0 -7
- package/dist/commands/execute/worker-dispatch.mjs +0 -64
- package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
- package/dist/commands/execute/worker-state.d.mts +0 -1
- package/dist/commands/execute/worker-state.mjs +0 -28
- package/dist/commands/execute/worker-state.mjs.map +0 -1
- package/dist/commands/execute/worktree-lookup.d.mts +0 -1
- package/dist/commands/execute/worktree-lookup.mjs +0 -92
- package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
- package/dist/commands/execute/worktree-status.d.mts +0 -1
- package/dist/commands/execute/worktree-status.mjs +0 -121
- package/dist/commands/execute/worktree-status.mjs.map +0 -1
- package/dist/commands/inspect/code-review.d.mts +0 -1
- package/dist/commands/inspect/code-review.mjs +0 -32
- package/dist/commands/inspect/code-review.mjs.map +0 -1
- package/dist/commands/inspect/container.d.mts +0 -1
- package/dist/commands/inspect/container.mjs +0 -25
- package/dist/commands/inspect/container.mjs.map +0 -1
- package/dist/commands/inspect/context-cost.d.mts +0 -1
- package/dist/commands/inspect/context-cost.mjs +0 -25
- package/dist/commands/inspect/context-cost.mjs.map +0 -1
- package/dist/commands/inspect/design-prep.d.mts +0 -1
- package/dist/commands/inspect/design-prep.mjs +0 -22
- package/dist/commands/inspect/design-prep.mjs.map +0 -1
- package/dist/commands/inspect/error-report.d.mts +0 -1
- package/dist/commands/inspect/error-report.mjs +0 -25
- package/dist/commands/inspect/error-report.mjs.map +0 -1
- package/dist/commands/inspect/error-zip.d.mts +0 -1
- package/dist/commands/inspect/error-zip.mjs +0 -24
- package/dist/commands/inspect/error-zip.mjs.map +0 -1
- package/dist/commands/inspect/log-report.d.mts +0 -1
- package/dist/commands/inspect/log-report.mjs +0 -26
- package/dist/commands/inspect/log-report.mjs.map +0 -1
- package/dist/commands/inspect/model-io.d.mts +0 -1
- package/dist/commands/inspect/model-io.mjs +0 -25
- package/dist/commands/inspect/model-io.mjs.map +0 -1
- package/dist/commands/inspect/profile-show.d.mts +0 -1
- package/dist/commands/inspect/profile-show.mjs +0 -28
- package/dist/commands/inspect/profile-show.mjs.map +0 -1
- package/dist/commands/inspect/recap.d.mts +0 -1
- package/dist/commands/inspect/recap.mjs +0 -30
- package/dist/commands/inspect/recap.mjs.map +0 -1
- package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
- package/dist/commands/inspect/resolve-task-key.mjs +0 -24
- package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
- package/dist/commands/inspect/rollup.d.mts +0 -1
- package/dist/commands/inspect/rollup.mjs +0 -25
- package/dist/commands/inspect/rollup.mjs.map +0 -1
- package/dist/commands/inspect/run-audit.d.mts +0 -1
- package/dist/commands/inspect/run-audit.mjs +0 -25
- package/dist/commands/inspect/run-audit.mjs.map +0 -1
- package/dist/commands/inspect/set-work-status.d.mts +0 -1
- package/dist/commands/inspect/set-work-status.mjs +0 -29
- package/dist/commands/inspect/set-work-status.mjs.map +0 -1
- package/dist/commands/inspect/stage-map.d.mts +0 -1
- package/dist/commands/inspect/stage-map.mjs +0 -131
- package/dist/commands/inspect/stage-map.mjs.map +0 -1
- package/dist/commands/inspect/task-list.d.mts +0 -1
- package/dist/commands/inspect/task-list.mjs +0 -149
- package/dist/commands/inspect/task-list.mjs.map +0 -1
- package/dist/commands/inspect/task-show.d.mts +0 -1
- package/dist/commands/inspect/task-show.mjs +0 -108
- package/dist/commands/inspect/task-show.mjs.map +0 -1
- package/dist/commands/inspect/time-report.d.mts +0 -1
- package/dist/commands/inspect/time-report.mjs +0 -24
- package/dist/commands/inspect/time-report.mjs.map +0 -1
- package/dist/commands/inspect/usage-report.d.mts +0 -1
- package/dist/commands/inspect/usage-report.mjs +0 -23
- package/dist/commands/inspect/usage-report.mjs.map +0 -1
- package/dist/commands/inspect/user-response.d.mts +0 -1
- package/dist/commands/inspect/user-response.mjs +0 -35
- package/dist/commands/inspect/user-response.mjs.map +0 -1
- package/dist/commands/inspect/worker-liveness.d.mts +0 -1
- package/dist/commands/inspect/worker-liveness.mjs +0 -45
- package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
- package/dist/commands/lifecycle/contract-check.d.mts +0 -1
- package/dist/commands/lifecycle/contract-check.mjs +0 -18
- package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
- package/dist/commands/lifecycle/migrate.d.mts +0 -1
- package/dist/commands/lifecycle/migrate.mjs +0 -30
- package/dist/commands/lifecycle/migrate.mjs.map +0 -1
- package/dist/commands/lifecycle/model.d.mts +0 -1
- package/dist/commands/lifecycle/model.mjs +0 -22
- package/dist/commands/lifecycle/model.mjs.map +0 -1
- package/dist/commands/manager.d.mts +0 -3
- package/dist/commands/manager.mjs +0 -50
- package/dist/commands/manager.mjs.map +0 -1
- package/dist/commands/report/agent-activity.d.mts +0 -1
- package/dist/commands/report/agent-activity.mjs +0 -20
- package/dist/commands/report/agent-activity.mjs.map +0 -1
- package/dist/commands/report/approval-decision.d.mts +0 -1
- package/dist/commands/report/approval-decision.mjs +0 -21
- package/dist/commands/report/approval-decision.mjs.map +0 -1
- package/dist/commands/report/design-snapshot.d.mts +0 -1
- package/dist/commands/report/design-snapshot.mjs +0 -19
- package/dist/commands/report/design-snapshot.mjs.map +0 -1
- package/dist/commands/report/finalize.d.mts +0 -4
- package/dist/commands/report/finalize.mjs +0 -64
- package/dist/commands/report/finalize.mjs.map +0 -1
- package/dist/commands/report/inject-report-index.d.mts +0 -1
- package/dist/commands/report/inject-report-index.mjs +0 -21
- package/dist/commands/report/inject-report-index.mjs.map +0 -1
- package/dist/commands/report/render-final-report.d.mts +0 -1
- package/dist/commands/report/render-final-report.mjs +0 -23
- package/dist/commands/report/render-final-report.mjs.map +0 -1
- package/dist/commands/report/render-views.d.mts +0 -1
- package/dist/commands/report/render-views.mjs +0 -27
- package/dist/commands/report/render-views.mjs.map +0 -1
- package/dist/commands/report/translate.d.mts +0 -1
- package/dist/commands/report/translate.mjs +0 -33
- package/dist/commands/report/translate.mjs.map +0 -1
- package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
- package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
- package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
- package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
- package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
- package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
- package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
- package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
- package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
- package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
- package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
- package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
- package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
- package/runtime/bin/okstra-central.sh +0 -152
- package/runtime/bin/okstra-incremental-carry.py +0 -10
- package/runtime/bin/okstra-incremental-scope.py +0 -10
- package/runtime/bin/okstra-team-reconcile.sh +0 -36
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
- package/runtime/python/okstra_ctl/batch.py +0 -60
- package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
- package/runtime/python/okstra_ctl/container_registry.py +0 -72
- package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
- package/runtime/python/okstra_ctl/resolver.py +0 -54
- package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
- package/runtime/python/okstra_ctl/tmux.py +0 -134
- package/runtime/python/okstra_ctl/worktree.py +0 -1099
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""materialize 를 요청한 신원이 이 run 이 발급한 것인지 확인한다.
|
|
2
|
+
|
|
3
|
+
run manifest 는 어떤 role 이 어떤 assignment 로 실행되는지를 이미 적어 두었다.
|
|
4
|
+
CLI 인자가 그것과 어긋나면 감사 기록이 실행과 달라지므로 거절한다. 이 검사가
|
|
5
|
+
없으면 매니페스트에 없는 role 로 프롬프트를 찍어 낼 수 있다.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Mapping
|
|
10
|
+
|
|
11
|
+
from ...worker_prompt_policy import (
|
|
12
|
+
CRITIC_DUTY_BY_ASSIGNMENT_SEGMENT,
|
|
13
|
+
resolve_prompt_plan_for_manifest,
|
|
14
|
+
)
|
|
15
|
+
from .inputs import AgentPromptCliError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _validate_run_identity(
|
|
19
|
+
manifest: Mapping[str, Any],
|
|
20
|
+
*,
|
|
21
|
+
worker_id: str,
|
|
22
|
+
dispatch_kind: str,
|
|
23
|
+
assignment_ref: str,
|
|
24
|
+
audience: str,
|
|
25
|
+
) -> None:
|
|
26
|
+
expected: str
|
|
27
|
+
if assignment_ref == "lead":
|
|
28
|
+
expected = "lead"
|
|
29
|
+
if worker_id != "lead":
|
|
30
|
+
raise AgentPromptCliError("lead assignment requires worker ID 'lead'")
|
|
31
|
+
elif assignment_ref == "translator":
|
|
32
|
+
expected = "translator"
|
|
33
|
+
if worker_id != "translator":
|
|
34
|
+
raise AgentPromptCliError("translator assignment requires worker ID 'translator'")
|
|
35
|
+
elif assignment_ref.startswith("critic/"):
|
|
36
|
+
scope = assignment_ref.split("/", 1)[1]
|
|
37
|
+
expected = CRITIC_DUTY_BY_ASSIGNMENT_SEGMENT.get(scope, "")
|
|
38
|
+
if not expected or dispatch_kind != "critic":
|
|
39
|
+
raise AgentPromptCliError("critic assignment identity is invalid")
|
|
40
|
+
# provider 대조는 여기 두지 않는다. critic 의 `worker_id` 는 배정 참조의
|
|
41
|
+
# 마지막 마디(`scope` / `acceptance`)이고 provider 이름이 아니다
|
|
42
|
+
# (`render.py` 의 로스터 행이 `assignment_ref.rsplit("/", 1)[-1]` 로 만든다).
|
|
43
|
+
# 종전에 있던 `provider != worker_id` 검사는 두 어휘를 비교해서 늘 참이 되는
|
|
44
|
+
# 형태였고, 설정을 run manifest 에서 찾다 못 찾아 조용히 건너뛰었기에
|
|
45
|
+
# 드러나지 않았다. 정본에서 읽게 고치면 모든 critic 디스패치가 거부된다.
|
|
46
|
+
else:
|
|
47
|
+
try:
|
|
48
|
+
expected = resolve_prompt_plan_for_manifest(
|
|
49
|
+
manifest=manifest,
|
|
50
|
+
worker_id=worker_id,
|
|
51
|
+
dispatch_kind=dispatch_kind,
|
|
52
|
+
).duty_audience
|
|
53
|
+
except ValueError as exc:
|
|
54
|
+
raise AgentPromptCliError(str(exc)) from exc
|
|
55
|
+
if audience != expected and _manifest_issued_role_execution(
|
|
56
|
+
manifest, audience=audience, assignment_ref=assignment_ref
|
|
57
|
+
):
|
|
58
|
+
# The prompt plan names a worker's role by worker id, so on an
|
|
59
|
+
# implementation run the executor's provider resolves to the
|
|
60
|
+
# executor role no matter which role execution is being targeted.
|
|
61
|
+
# But the manifest issues one role execution per role, and a
|
|
62
|
+
# provider serving as both executor and verifier gets two — a
|
|
63
|
+
# normal roster, since the verifier contract accepts reusing the
|
|
64
|
+
# executor's model behind its own session. Refusing the second
|
|
65
|
+
# audience made a role execution okstra had itself issued
|
|
66
|
+
# undispatchable, which costs the run an independent verifier.
|
|
67
|
+
expected = audience
|
|
68
|
+
if audience != expected:
|
|
69
|
+
raise AgentPromptCliError(
|
|
70
|
+
f"audience {audience!r} does not match required audience {expected!r}"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _manifest_issued_role_execution(
|
|
75
|
+
manifest: Mapping[str, Any],
|
|
76
|
+
*,
|
|
77
|
+
audience: str,
|
|
78
|
+
assignment_ref: str,
|
|
79
|
+
) -> bool:
|
|
80
|
+
"""Whether this run issued a role execution for that audience's role.
|
|
81
|
+
|
|
82
|
+
Keyed on the manifest's own rows, not on what the prompt plan infers from
|
|
83
|
+
a worker id: the question is whether okstra created the execution being
|
|
84
|
+
targeted, and only the manifest answers that.
|
|
85
|
+
"""
|
|
86
|
+
from ...domain.role import RoleCatalogError, role_for_duty
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
role = role_for_duty(audience)
|
|
90
|
+
except RoleCatalogError:
|
|
91
|
+
return False
|
|
92
|
+
assignments = manifest.get("invocationAssignments")
|
|
93
|
+
assignment = (
|
|
94
|
+
assignments.get(assignment_ref) if isinstance(assignments, Mapping) else None
|
|
95
|
+
)
|
|
96
|
+
provider = (
|
|
97
|
+
assignment.get("provider") if isinstance(assignment, Mapping) else None
|
|
98
|
+
)
|
|
99
|
+
if not provider:
|
|
100
|
+
return False
|
|
101
|
+
executions = manifest.get("roleExecutions")
|
|
102
|
+
return any(
|
|
103
|
+
isinstance(row, Mapping)
|
|
104
|
+
and row.get("role") == role
|
|
105
|
+
and row.get("provider") == provider
|
|
106
|
+
for row in (executions if isinstance(executions, list) else [])
|
|
107
|
+
)
|
|
@@ -67,7 +67,7 @@ WORKER_PROFILE_SECTIONS_BY_TASK_TYPE = {
|
|
|
67
67
|
"Brief consumption",
|
|
68
68
|
"Worker planning procedure",
|
|
69
69
|
"Pre-planning context exploration",
|
|
70
|
-
"Design principles applied
|
|
70
|
+
"Design principles applied to realization review",
|
|
71
71
|
),
|
|
72
72
|
"final-verification": (
|
|
73
73
|
"Worker verification procedure",
|
|
@@ -102,8 +102,17 @@ def build_analysis_packet(
|
|
|
102
102
|
fix_history_text: str = "",
|
|
103
103
|
stage_ledger_json: str = "",
|
|
104
104
|
stage_ledger_notice: str = "",
|
|
105
|
+
prior_planning_summary: str = "",
|
|
105
106
|
) -> str:
|
|
106
|
-
"""Return the primary compact input for Claude/Codex/Antigravity analysers.
|
|
107
|
+
"""Return the primary compact input for Claude/Codex/Antigravity analysers.
|
|
108
|
+
|
|
109
|
+
`fix_history_text`, `stage_ledger_json` / `stage_ledger_notice`, and
|
|
110
|
+
`prior_planning_summary` are pre-rendered by their own modules
|
|
111
|
+
(`fix_cycles`, `stage_ledger`, `prior_planning`) because each is assembled
|
|
112
|
+
from disk state this file does not own. Each arrives already narrowed, and
|
|
113
|
+
an empty string means the caller found nothing to carry — the matching
|
|
114
|
+
block is then omitted rather than rendered saying "none".
|
|
115
|
+
"""
|
|
107
116
|
brief_text = task_brief_path.read_text(encoding="utf-8")
|
|
108
117
|
profile_text = analysis_profile_path.read_text(encoding="utf-8")
|
|
109
118
|
reference_text = _read_optional(reference_expectations_path)
|
|
@@ -126,6 +135,7 @@ def build_analysis_packet(
|
|
|
126
135
|
parts.extend(_fix_history_block(fix_history_text))
|
|
127
136
|
parts.extend(_stage_ledger_block(stage_ledger_json))
|
|
128
137
|
parts.extend(_stage_ledger_unavailable_block(stage_ledger_notice))
|
|
138
|
+
parts.extend(_prior_planning_block(prior_planning_summary))
|
|
129
139
|
parts.extend(_clarification_block(clarification_text))
|
|
130
140
|
parts.extend(_directive_block(directive))
|
|
131
141
|
body = "\n".join(part.rstrip() for part in parts).rstrip() + "\n"
|
|
@@ -334,8 +344,11 @@ def _stage_ledger_block(stage_ledger_json: str) -> list[str]:
|
|
|
334
344
|
"authority is the latter.",
|
|
335
345
|
"",
|
|
336
346
|
"A `planDivergence` entry means the two plans disagree about a",
|
|
337
|
-
"completed stage
|
|
338
|
-
"
|
|
347
|
+
"completed stage, or that the plan the completed stages were built",
|
|
348
|
+
"against could not be read so that comparison never ran. Treat either",
|
|
349
|
+
"as a blocker for any new stage number and report it; do not resolve",
|
|
350
|
+
"it by choosing one of the two yourself. Completed stages built",
|
|
351
|
+
"against an earlier plan are not a divergence.",
|
|
339
352
|
"",
|
|
340
353
|
"```json",
|
|
341
354
|
stage_ledger_json.strip(),
|
|
@@ -371,6 +384,36 @@ def _stage_ledger_unavailable_block(notice: str) -> list[str]:
|
|
|
371
384
|
]
|
|
372
385
|
|
|
373
386
|
|
|
387
|
+
def _prior_planning_block(prior_planning_summary: str) -> list[str]:
|
|
388
|
+
"""직전 계획 run 의 맥락. 직전 run 이 없으면 블록 자체가 없다.
|
|
389
|
+
|
|
390
|
+
머리글이 이 블록의 용도를 스스로 말한다. 그러지 않으면 워커는 표에 실린
|
|
391
|
+
계획을 승인된 것으로 읽고 그대로 옮겨 적는다 — 이 블록이 실리는 run 은
|
|
392
|
+
직전 계획이 **승인에 도달하지 못해** 다시 도는 run 이므로 그 독해는 틀렸다.
|
|
393
|
+
"""
|
|
394
|
+
if not prior_planning_summary.strip():
|
|
395
|
+
return []
|
|
396
|
+
return [
|
|
397
|
+
"",
|
|
398
|
+
"## Prior Planning Run",
|
|
399
|
+
"",
|
|
400
|
+
"What this task's previous implementation-planning run recorded. It is",
|
|
401
|
+
"here so that stages the previous plan already settled stay textually",
|
|
402
|
+
"stable across the re-run, and so settled analysis is not derived from",
|
|
403
|
+
"the brief a second time under new `P-*` numbers.",
|
|
404
|
+
"",
|
|
405
|
+
"It is NOT an approved plan and NOT an instruction to copy. The",
|
|
406
|
+
"previous run did not reach approval — that is why this run exists —",
|
|
407
|
+
"so every line below is still open to revision. Where the brief, the",
|
|
408
|
+
"directive, or a clarification answer disagrees with it, they win, and",
|
|
409
|
+
"a stage you are changing is rewritten rather than carried. Read the",
|
|
410
|
+
"report at the path below for anything this summary leaves out.",
|
|
411
|
+
"",
|
|
412
|
+
prior_planning_summary.strip(),
|
|
413
|
+
"",
|
|
414
|
+
]
|
|
415
|
+
|
|
416
|
+
|
|
374
417
|
def _clarification_block(clarification_text: str) -> list[str]:
|
|
375
418
|
if not clarification_text.strip():
|
|
376
419
|
return []
|
|
@@ -6,12 +6,36 @@ import json
|
|
|
6
6
|
import re
|
|
7
7
|
import sys
|
|
8
8
|
from copy import deepcopy
|
|
9
|
+
from functools import lru_cache
|
|
9
10
|
from dataclasses import dataclass
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Any, Mapping, Sequence
|
|
12
13
|
|
|
14
|
+
from .clarification_items.sidecars import (
|
|
15
|
+
attached_response_sections,
|
|
16
|
+
response_source_report,
|
|
17
|
+
)
|
|
18
|
+
from .user_response_values import parse_user_response_entries
|
|
13
19
|
from .convergence_store import write_json_atomic
|
|
20
|
+
from .final_report_schema import load_schema_version
|
|
14
21
|
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
22
|
+
from .report_contract import CURRENT_REPORT_SCHEMA_VERSION
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@lru_cache(maxsize=None)
|
|
26
|
+
def _schema_enum(definition: str) -> frozenset[str]:
|
|
27
|
+
"""리포트 스키마의 ``$defs`` enum 을 그대로 읽는다.
|
|
28
|
+
|
|
29
|
+
이 원장의 행은 그대로 final-report 의 ``clarificationItems`` 가 된다. 값 목록을
|
|
30
|
+
여기에 다시 타이핑하면 스키마와 따로 흐르므로 정본에서 읽는다.
|
|
31
|
+
"""
|
|
32
|
+
schema = load_schema_version(CURRENT_REPORT_SCHEMA_VERSION)
|
|
33
|
+
values = (schema.get("$defs") or {}).get(definition, {}).get("enum")
|
|
34
|
+
if not isinstance(values, list) or not values:
|
|
35
|
+
raise ApprovalDecisionError(
|
|
36
|
+
f"report schema has no enum for {definition!r}"
|
|
37
|
+
)
|
|
38
|
+
return frozenset(str(value) for value in values)
|
|
15
39
|
|
|
16
40
|
|
|
17
41
|
DISPOSITIONS = frozenset({"select", "accept-risk", "request-revision", "reject"})
|
|
@@ -127,6 +151,23 @@ def _validate_option_set(
|
|
|
127
151
|
)
|
|
128
152
|
|
|
129
153
|
|
|
154
|
+
def _reject_off_enum(value: str, field: str, definition: str) -> None:
|
|
155
|
+
"""스키마 밖 값을 원장 진입 지점에서 거절한다.
|
|
156
|
+
|
|
157
|
+
종전에는 두 provenance 필드가 비어 있지 않기만 하면 통과했다. 리포트 조립이
|
|
158
|
+
스키마로 잡아 내기는 하지만 그때는 이미 레코드를 디스크에 쓴 뒤라, 잘못된 값이
|
|
159
|
+
원장과 리포트 양쪽에 남아 이후의 모든 읽기(`okstra user-response`)를 막았다.
|
|
160
|
+
"""
|
|
161
|
+
if not value:
|
|
162
|
+
return
|
|
163
|
+
allowed = _schema_enum(definition)
|
|
164
|
+
if value not in allowed:
|
|
165
|
+
raise ApprovalDecisionError(
|
|
166
|
+
f"invalid {field}: {value!r}. Allowed values: "
|
|
167
|
+
+ ", ".join(sorted(allowed))
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
130
171
|
def _decision_row(
|
|
131
172
|
*, clarification_id: str, ticket_id: str, statement: str,
|
|
132
173
|
expected_form: str, classification: str, origin: str,
|
|
@@ -135,6 +176,10 @@ def _decision_row(
|
|
|
135
176
|
) -> dict[str, Any]:
|
|
136
177
|
if classification not in CLASSIFICATIONS:
|
|
137
178
|
raise ApprovalDecisionError(f"invalid classification: {classification}")
|
|
179
|
+
_reject_off_enum(origin, "origin", "ClarificationOrigin")
|
|
180
|
+
_reject_off_enum(
|
|
181
|
+
user_confirmation, "user_confirmation", "ClarificationUserConfirmation"
|
|
182
|
+
)
|
|
138
183
|
if recommended_disposition not in DISPOSITIONS:
|
|
139
184
|
raise ApprovalDecisionError(
|
|
140
185
|
f"invalid recommended_disposition: {recommended_disposition}"
|
|
@@ -260,8 +305,21 @@ def _add_open_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
260
305
|
parser.add_argument(f"--option-{flag}", action="append", default=[])
|
|
261
306
|
|
|
262
307
|
|
|
308
|
+
_CLI_EPILOG = r"""Usage:
|
|
309
|
+
okstra approval-decision open --ledger <path> [decision fields]
|
|
310
|
+
okstra approval-decision resolve --ledger <path> --clarification-id <C-NNN> [resolution fields]
|
|
311
|
+
okstra approval-decision carry --ledger <path> --from-responses <clarification-response.md> --clarification-id <C-NNN> [--clarification-id <C-NNN> ...]
|
|
312
|
+
okstra approval-decision carry --ledger <path> --source-ledger <path> --source-run-ref <ref> --clarification-id <C-NNN>
|
|
313
|
+
"""
|
|
314
|
+
_CLI_DESCRIPTION = "Record lead-owned approval inputs."
|
|
315
|
+
|
|
316
|
+
|
|
263
317
|
def _parser() -> argparse.ArgumentParser:
|
|
264
|
-
parser = argparse.ArgumentParser(
|
|
318
|
+
parser = argparse.ArgumentParser(
|
|
319
|
+
description=_CLI_DESCRIPTION,
|
|
320
|
+
epilog=_CLI_EPILOG,
|
|
321
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
322
|
+
prog="okstra approval-decision")
|
|
265
323
|
commands = parser.add_subparsers(dest="command", required=True)
|
|
266
324
|
opened = commands.add_parser("open")
|
|
267
325
|
opened.add_argument("--ledger", type=Path, required=True)
|
|
@@ -275,9 +333,38 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
275
333
|
resolved.add_argument("--check-ref", action="append", default=[])
|
|
276
334
|
carried = commands.add_parser("carry")
|
|
277
335
|
carried.add_argument("--ledger", type=Path, required=True)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
336
|
+
source = carried.add_mutually_exclusive_group(required=True)
|
|
337
|
+
source.add_argument(
|
|
338
|
+
"--from-responses",
|
|
339
|
+
type=Path,
|
|
340
|
+
help=(
|
|
341
|
+
"attached user-responses bundle (`instruction-set/"
|
|
342
|
+
"clarification-response.md`). It is the source of truth for the "
|
|
343
|
+
"answer; the question's own fields come from the report each "
|
|
344
|
+
"response section names. Task-level and cumulative, so no prior "
|
|
345
|
+
"run seq has to be located."
|
|
346
|
+
),
|
|
347
|
+
)
|
|
348
|
+
source.add_argument(
|
|
349
|
+
"--source-ledger",
|
|
350
|
+
type=Path,
|
|
351
|
+
help="a prior run's approval ledger, when it is still on disk",
|
|
352
|
+
)
|
|
353
|
+
carried.add_argument(
|
|
354
|
+
"--source-run-ref",
|
|
355
|
+
default="",
|
|
356
|
+
help=(
|
|
357
|
+
"provenance recorded on the carried row. Required with "
|
|
358
|
+
"--source-ledger; with --from-responses it defaults to the "
|
|
359
|
+
"response section the answer came from."
|
|
360
|
+
),
|
|
361
|
+
)
|
|
362
|
+
carried.add_argument(
|
|
363
|
+
"--clarification-id",
|
|
364
|
+
action="append",
|
|
365
|
+
required=True,
|
|
366
|
+
help="repeat to carry several ids in one call",
|
|
367
|
+
)
|
|
281
368
|
return parser
|
|
282
369
|
|
|
283
370
|
|
|
@@ -291,13 +378,174 @@ def _open_from_args(args: argparse.Namespace) -> None:
|
|
|
291
378
|
open_decision(**values, options=_options_from_args(args))
|
|
292
379
|
|
|
293
380
|
|
|
381
|
+
# `kind: decision` 인 행은 스키마가 옵션 메뉴를 요구하므로 함께 가져온다.
|
|
382
|
+
# `resolution` 은 가져오지 않는다 — 그 `checkRefs` 는 답을 기록한 run 의 활동
|
|
383
|
+
# 행이고, 이월 행은 `answered` 로 실린다.
|
|
384
|
+
_QUESTION_FIELDS = (
|
|
385
|
+
"id", "ticketId", "kind", "statement", "expectedForm", "blocks", "origin",
|
|
386
|
+
"options",
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _responses_index(path: Path) -> dict[str, tuple[Any, str, str, str]]:
|
|
391
|
+
"""묶음 파일의 답을 ``{id: (entry, 구간 이름, 리포트 기록 이름, task-type)}`` 로.
|
|
392
|
+
|
|
393
|
+
같은 id 를 여러 run 이 답했으면 뒤 구간이 이긴다 — 묶음은 시간순으로 쌓이고
|
|
394
|
+
최신 답이 정본이다.
|
|
395
|
+
"""
|
|
396
|
+
try:
|
|
397
|
+
text = path.read_text(encoding="utf-8")
|
|
398
|
+
except OSError as exc:
|
|
399
|
+
raise ApprovalDecisionError(f"cannot read {path}: {exc}") from exc
|
|
400
|
+
sections = attached_response_sections(text)
|
|
401
|
+
if not sections:
|
|
402
|
+
raise ApprovalDecisionError(
|
|
403
|
+
f"not an attached user-responses bundle (no `## user-response-*.md` "
|
|
404
|
+
f"section): {path}"
|
|
405
|
+
)
|
|
406
|
+
index: dict[str, tuple[Any, str, str, str]] = {}
|
|
407
|
+
for name, body in sections:
|
|
408
|
+
report = response_source_report(body)
|
|
409
|
+
task_type = _frontmatter_task_type(body)
|
|
410
|
+
for entry in parse_user_response_entries(body):
|
|
411
|
+
index[entry.response_id] = (entry, name, report, task_type)
|
|
412
|
+
return index
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _frontmatter_task_type(section_body: str) -> str:
|
|
416
|
+
match = re.search(r"^task-type:\s*(\S.*?)\s*$", section_body, re.MULTILINE)
|
|
417
|
+
return match.group(1) if match else ""
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _prior_question_row(
|
|
421
|
+
responses_path: Path, clarification_id: str, report_name: str, task_type: str,
|
|
422
|
+
) -> dict[str, Any]:
|
|
423
|
+
"""질문 메타데이터의 출처는 그 질문을 낸 리포트다.
|
|
424
|
+
|
|
425
|
+
묶음 파일은 답만 담는다. `ticketId`·`statement`·`expectedForm` 은 스키마가
|
|
426
|
+
요구하는데 묶음에 없으므로, 지어내는 대신 구간 frontmatter 가 지목한
|
|
427
|
+
리포트에서 읽는다. 그 리드가 seq 를 찾아 헤매지 않게 하는 것이 이 경로의
|
|
428
|
+
요점이다.
|
|
429
|
+
"""
|
|
430
|
+
if not report_name or not task_type:
|
|
431
|
+
raise ApprovalDecisionError(
|
|
432
|
+
f"{clarification_id}: its response section names no source report; "
|
|
433
|
+
"carry it with --source-ledger instead"
|
|
434
|
+
)
|
|
435
|
+
task_root = responses_path.resolve().parent.parent
|
|
436
|
+
report_path = (
|
|
437
|
+
task_root / "runs" / task_type / "reports" / Path(report_name).name
|
|
438
|
+
)
|
|
439
|
+
if not report_path.is_file():
|
|
440
|
+
raise ApprovalDecisionError(
|
|
441
|
+
f"{clarification_id}: source report not found: {report_path}"
|
|
442
|
+
)
|
|
443
|
+
try:
|
|
444
|
+
data = load_owned_object(report_path, artifact="prior final report")
|
|
445
|
+
except (JsonBoundaryError, OSError) as exc:
|
|
446
|
+
raise ApprovalDecisionError(f"cannot read {report_path}: {exc}") from exc
|
|
447
|
+
rows = data.get("clarificationItems") if isinstance(data, Mapping) else None
|
|
448
|
+
match = next(
|
|
449
|
+
(
|
|
450
|
+
row for row in (rows or [])
|
|
451
|
+
if isinstance(row, Mapping) and row.get("id") == clarification_id
|
|
452
|
+
),
|
|
453
|
+
None,
|
|
454
|
+
)
|
|
455
|
+
if match is None:
|
|
456
|
+
raise ApprovalDecisionError(
|
|
457
|
+
f"{clarification_id}: not defined in {report_path.name}"
|
|
458
|
+
)
|
|
459
|
+
row = {key: match[key] for key in _QUESTION_FIELDS if key in match}
|
|
460
|
+
if isinstance(row.get("options"), list):
|
|
461
|
+
row["options"] = [
|
|
462
|
+
_contract_v3_option(option, clarification_id)
|
|
463
|
+
for option in row["options"]
|
|
464
|
+
if isinstance(option, Mapping)
|
|
465
|
+
]
|
|
466
|
+
if "approvalContext" in match:
|
|
467
|
+
row["approval"] = match["approvalContext"]
|
|
468
|
+
return row
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
_REACH_TOKENS = ("in-repo", "cross-repo")
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def _contract_v3_option(option: Mapping[str, Any], clarification_id: str) -> dict[str, Any]:
|
|
475
|
+
"""계약 2.0 의 `scopeImpact` 를 3.0 의 `reach` + `scopeEffects` 로 되돌린다.
|
|
476
|
+
|
|
477
|
+
두 열거는 정확히 분할 관계다 — 2.0 의 토큰 집합
|
|
478
|
+
`{in-repo, cross-repo, new-schema, deferrable}` 이 3.0 에서 `reach`
|
|
479
|
+
(`in-repo|cross-repo`, 정확히 하나)와 `scopeEffects`(나머지)로 갈렸다.
|
|
480
|
+
그래서 이 변환은 값을 지어내지 않는다. 이미 3.0 모양이면 그대로 둔다.
|
|
481
|
+
"""
|
|
482
|
+
row = dict(option)
|
|
483
|
+
if "reach" in row:
|
|
484
|
+
row.pop("scopeImpact", None)
|
|
485
|
+
return row
|
|
486
|
+
tokens = row.pop("scopeImpact", None)
|
|
487
|
+
if not isinstance(tokens, list):
|
|
488
|
+
raise ApprovalDecisionError(
|
|
489
|
+
f"{clarification_id}: an option states neither `reach` nor "
|
|
490
|
+
"`scopeImpact`; carry it with --source-ledger instead"
|
|
491
|
+
)
|
|
492
|
+
reach = [token for token in tokens if token in _REACH_TOKENS]
|
|
493
|
+
if len(reach) != 1:
|
|
494
|
+
raise ApprovalDecisionError(
|
|
495
|
+
f"{clarification_id}: an option's `scopeImpact` names {len(reach)} "
|
|
496
|
+
"reach token(s); exactly one is needed to state `reach`"
|
|
497
|
+
)
|
|
498
|
+
row["reach"] = reach[0]
|
|
499
|
+
effects = [token for token in tokens if token not in _REACH_TOKENS]
|
|
500
|
+
if effects:
|
|
501
|
+
row["scopeEffects"] = effects
|
|
502
|
+
return row
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _carry_from_responses(args: argparse.Namespace) -> None:
|
|
506
|
+
index = _responses_index(args.from_responses)
|
|
507
|
+
for clarification_id in args.clarification_id:
|
|
508
|
+
found = index.get(clarification_id)
|
|
509
|
+
if found is None:
|
|
510
|
+
raise ApprovalDecisionError(
|
|
511
|
+
f"clarification not answered in {args.from_responses}: "
|
|
512
|
+
f"{clarification_id}"
|
|
513
|
+
)
|
|
514
|
+
entry, section, report_name, task_type = found
|
|
515
|
+
if not entry.value.strip():
|
|
516
|
+
raise ApprovalDecisionError(
|
|
517
|
+
f"{clarification_id}: its answer in {section} has no Value"
|
|
518
|
+
)
|
|
519
|
+
decision = _prior_question_row(
|
|
520
|
+
args.from_responses, clarification_id, report_name, task_type
|
|
521
|
+
)
|
|
522
|
+
# 답이 정본인 자리는 여기다. `resolutionInput` 은 쓰지 않는다 —
|
|
523
|
+
# 그 모양은 `checkRefs` 로 이번 run 의 활동 행을 요구하는데, 이월된
|
|
524
|
+
# 답에는 이번 run 의 활동이 없다(`report_assembly` 의 이월 분기).
|
|
525
|
+
decision["userConfirmation"] = "asked-and-answered"
|
|
526
|
+
decision["userInput"] = entry.value
|
|
527
|
+
carry_decision(
|
|
528
|
+
args.ledger,
|
|
529
|
+
source_run_ref=args.source_run_ref or section,
|
|
530
|
+
decision=decision,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
|
|
294
534
|
def _carry_from_args(args: argparse.Namespace) -> None:
|
|
295
535
|
source = _read_ledger(args.source_ledger)
|
|
296
536
|
rows = source.get("activeClarifications") or []
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
537
|
+
for clarification_id in args.clarification_id:
|
|
538
|
+
matches = [
|
|
539
|
+
row for row in rows
|
|
540
|
+
if isinstance(row, dict) and row.get("id") == clarification_id
|
|
541
|
+
]
|
|
542
|
+
if len(matches) != 1:
|
|
543
|
+
raise ApprovalDecisionError(
|
|
544
|
+
f"source clarification not found: {clarification_id}"
|
|
545
|
+
)
|
|
546
|
+
carry_decision(
|
|
547
|
+
args.ledger, source_run_ref=args.source_run_ref, decision=matches[0]
|
|
548
|
+
)
|
|
301
549
|
|
|
302
550
|
|
|
303
551
|
def _run(args: argparse.Namespace) -> None:
|
|
@@ -309,7 +557,11 @@ def _run(args: argparse.Namespace) -> None:
|
|
|
309
557
|
user_text=args.user_text, user_response_ref=args.user_response_ref,
|
|
310
558
|
check_refs=args.check_ref,
|
|
311
559
|
)
|
|
560
|
+
elif args.from_responses:
|
|
561
|
+
_carry_from_responses(args)
|
|
312
562
|
else:
|
|
563
|
+
if not args.source_run_ref:
|
|
564
|
+
raise ApprovalDecisionError("--source-ledger requires --source-run-ref")
|
|
313
565
|
_carry_from_args(args)
|
|
314
566
|
|
|
315
567
|
|
|
@@ -91,18 +91,6 @@ class AssignmentContext:
|
|
|
91
91
|
environment: AssignmentEnvironment
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
@dataclass(frozen=True)
|
|
95
|
-
class RoleCountSelection:
|
|
96
|
-
role: str
|
|
97
|
-
count: int
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
@dataclass(frozen=True)
|
|
101
|
-
class RoleModelSelection:
|
|
102
|
-
role: str
|
|
103
|
-
model_refs: tuple[str, ...]
|
|
104
|
-
|
|
105
|
-
|
|
106
94
|
@dataclass(frozen=True)
|
|
107
95
|
class RoleInstance:
|
|
108
96
|
role: str
|
|
@@ -3,13 +3,17 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import json
|
|
6
|
-
from dataclasses import dataclass
|
|
6
|
+
from dataclasses import dataclass, replace
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Mapping, Sequence
|
|
10
10
|
|
|
11
11
|
from .domain.worker_stream import host_event_row
|
|
12
|
-
from .execution_identity import
|
|
12
|
+
from .execution_identity import (
|
|
13
|
+
Attempt,
|
|
14
|
+
ExecutionManifestError,
|
|
15
|
+
MutationRecoveryDecision,
|
|
16
|
+
)
|
|
13
17
|
from .execution_manifest import read_execution_manifest
|
|
14
18
|
from .execution_manifest import _replace_manifest, _write_manifest_over_existing
|
|
15
19
|
from .jsonl import append_jsonl
|
|
@@ -221,15 +225,10 @@ def finalize_attempt(
|
|
|
221
225
|
raise AttemptEvidenceError("attempt to seal is unknown")
|
|
222
226
|
existing = manifest.attempts[index]
|
|
223
227
|
if terminal.interrupted_before_record:
|
|
224
|
-
closed =
|
|
225
|
-
|
|
226
|
-
attempt=existing.attempt,
|
|
227
|
-
started_at=existing.started_at,
|
|
228
|
+
closed = replace(
|
|
229
|
+
existing,
|
|
228
230
|
finished_at=_utc_now(),
|
|
229
231
|
status="evidence-unsealed",
|
|
230
|
-
result_path=existing.result_path,
|
|
231
|
-
error_path=existing.error_path,
|
|
232
|
-
change_summary=dict(existing.change_summary),
|
|
233
232
|
evidence_seal_ref=None,
|
|
234
233
|
)
|
|
235
234
|
attempts = list(manifest.attempts)
|
|
@@ -252,15 +251,10 @@ def finalize_attempt(
|
|
|
252
251
|
if not _same_observation(existing_seal, terminal):
|
|
253
252
|
raise AttemptEvidenceError("attempt already sealed with different observation")
|
|
254
253
|
return existing_seal
|
|
255
|
-
closed =
|
|
256
|
-
|
|
257
|
-
attempt=existing.attempt,
|
|
258
|
-
started_at=existing.started_at,
|
|
254
|
+
closed = replace(
|
|
255
|
+
existing,
|
|
259
256
|
finished_at=existing.finished_at or _utc_now(),
|
|
260
257
|
status=terminal.status,
|
|
261
|
-
result_path=existing.result_path,
|
|
262
|
-
error_path=existing.error_path,
|
|
263
|
-
change_summary=dict(existing.change_summary),
|
|
264
258
|
evidence_seal_ref=seal.seal_ref,
|
|
265
259
|
)
|
|
266
260
|
attempts = list(manifest.attempts)
|
|
@@ -285,7 +279,6 @@ def reject_unsealed_attempt(
|
|
|
285
279
|
task_key: str,
|
|
286
280
|
):
|
|
287
281
|
"""Close an unsealed attempt and record a cleanup-only reject."""
|
|
288
|
-
from .mutation_recovery import MutationRecoveryDecision
|
|
289
282
|
|
|
290
283
|
invocation_ref, attempt_number = _split_attempt_ref(attempt_ref)
|
|
291
284
|
with task_mutex(task_key):
|
|
@@ -301,15 +294,10 @@ def reject_unsealed_attempt(
|
|
|
301
294
|
if index is None:
|
|
302
295
|
raise AttemptEvidenceError("attempt to reject is unknown")
|
|
303
296
|
existing = manifest.attempts[index]
|
|
304
|
-
closed =
|
|
305
|
-
|
|
306
|
-
attempt=existing.attempt,
|
|
307
|
-
started_at=existing.started_at,
|
|
297
|
+
closed = replace(
|
|
298
|
+
existing,
|
|
308
299
|
finished_at=existing.finished_at or _utc_now(),
|
|
309
300
|
status="evidence-unsealed",
|
|
310
|
-
result_path=existing.result_path,
|
|
311
|
-
error_path=existing.error_path,
|
|
312
|
-
change_summary=dict(existing.change_summary),
|
|
313
301
|
evidence_seal_ref=None,
|
|
314
302
|
)
|
|
315
303
|
decision = MutationRecoveryDecision(
|