okstra 0.186.7 → 0.188.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 +107 -40
- 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 +39 -38
- 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 +47 -22
- 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/adapters/cmux.md +3 -3
- package/runtime/prompts/lead/convergence.md +99 -88
- 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 +35 -5
- 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 +63 -22
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +20 -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} +16 -2
- 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 +228 -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 +605 -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 +287 -10
- 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 +32 -4
- package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
- package/runtime/python/okstra_ctl/convergence.py +107 -16
- 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 +236 -49
- package/runtime/python/okstra_ctl/dispatch_state.py +417 -41
- 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 +28 -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/exact_coverage.py +5 -0
- 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 +319 -17
- 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 +32 -13
- 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 +4 -8
- 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 +213 -16
- 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 +220 -34
- package/runtime/python/okstra_ctl/report_projections.py +8 -2
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +393 -1
- package/runtime/python/okstra_ctl/report_translation.py +2 -35
- package/runtime/python/okstra_ctl/report_view_artifacts.py +42 -2
- 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/verdict_blocks.py +28 -8
- 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 +58 -17
- 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/antigravity.py +50 -12
- 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 +171 -36
- 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 +23 -8
- 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 +3 -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 +1363 -2643
- 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
|
@@ -8,6 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import argparse
|
|
10
10
|
import json
|
|
11
|
+
import os
|
|
11
12
|
import re
|
|
12
13
|
import sys
|
|
13
14
|
from pathlib import Path
|
|
@@ -15,7 +16,7 @@ from typing import Iterable
|
|
|
15
16
|
|
|
16
17
|
from okstra_ctl.paths import RunRef, runs_dir_of, task_manifest_file
|
|
17
18
|
from okstra_ctl.task_target import resolve_task_root, project_rel
|
|
18
|
-
from okstra_ctl.json_boundary import load_owned_object
|
|
19
|
+
from okstra_ctl.json_boundary import JsonBoundaryError, load_owned_object
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
INPUT_FILES = (
|
|
@@ -85,7 +86,7 @@ def _load_json(path: Path) -> dict:
|
|
|
85
86
|
return {}
|
|
86
87
|
try:
|
|
87
88
|
data = load_owned_object(path, artifact="context cost input")
|
|
88
|
-
except
|
|
89
|
+
except JsonBoundaryError:
|
|
89
90
|
return {}
|
|
90
91
|
return data if isinstance(data, dict) else {}
|
|
91
92
|
|
|
@@ -143,6 +144,19 @@ def _is_timestamped_legacy_artifact(path: Path) -> bool:
|
|
|
143
144
|
return bool(TIMESTAMPED_ARTIFACT_RE.search(path.name))
|
|
144
145
|
|
|
145
146
|
|
|
147
|
+
def _okstra_asset_home() -> Path:
|
|
148
|
+
"""자산을 읽을 okstra 홈. `OKSTRA_HOME` 이 있으면 그쪽이다.
|
|
149
|
+
|
|
150
|
+
종전에는 `Path.home() / ".okstra"` 를 그대로 박아, 홈을 갈아끼운 호출자가
|
|
151
|
+
실제 사용자 설치본을 읽었다. `find_asset_root` 의 docstring 이 규정한 대로
|
|
152
|
+
`OKSTRA_HOME` 이 유일한 격리 이음새이므로 여기서도 그것을 먼저 본다.
|
|
153
|
+
실측: e2e scenario-23 이 샌드박스 홈을 지정하고도 `~/.okstra/templates/`
|
|
154
|
+
를 보고해, 설치본이 있는 기계와 없는 기계의 출력이 달랐다.
|
|
155
|
+
"""
|
|
156
|
+
override = os.environ.get("OKSTRA_HOME")
|
|
157
|
+
return Path(override) if override else Path.home() / ".okstra"
|
|
158
|
+
|
|
159
|
+
|
|
146
160
|
def _installed_or_dev(installed: Path, dev_relative: str) -> Path:
|
|
147
161
|
"""Prefer the user-machine install (what production runs read); fall back
|
|
148
162
|
to the repo dev tree when running from an uninstalled checkout."""
|
|
@@ -153,7 +167,7 @@ def _installed_or_dev(installed: Path, dev_relative: str) -> Path:
|
|
|
153
167
|
|
|
154
168
|
def _runtime_template(filename: str) -> Path:
|
|
155
169
|
return _installed_or_dev(
|
|
156
|
-
|
|
170
|
+
_okstra_asset_home() / "templates" / filename,
|
|
157
171
|
f"templates/{filename}",
|
|
158
172
|
)
|
|
159
173
|
|
|
@@ -223,7 +237,7 @@ def _skill_assets_metric() -> dict:
|
|
|
223
237
|
skill bodies + worker agent specs. These dominate the fixed per-run
|
|
224
238
|
instruction footprint and are the prompt-diet ranking input."""
|
|
225
239
|
entries = []
|
|
226
|
-
okstra_home =
|
|
240
|
+
okstra_home = _okstra_asset_home()
|
|
227
241
|
claude_home = Path.home() / ".claude"
|
|
228
242
|
for name in HOT_PATH_LEAD_RESOURCES:
|
|
229
243
|
path = _installed_or_dev(
|
|
@@ -481,8 +495,22 @@ def analyze_task_bundle(task_root: Path, project_root: Path) -> dict:
|
|
|
481
495
|
}
|
|
482
496
|
|
|
483
497
|
|
|
498
|
+
_CLI_EPILOG = r"""Usage:
|
|
499
|
+
okstra context-cost <task-root>
|
|
500
|
+
okstra context-cost <task-key> --project-root <dir>
|
|
501
|
+
okstra context-cost <task-key> --cwd <dir>
|
|
502
|
+
|
|
503
|
+
Output: JSON with task file counts, current-run counts, lead Phase 1 read
|
|
504
|
+
surface, analysis-worker initial read surface, and report-writer synthesis
|
|
505
|
+
surface. This is read-only; it never mutates task artifacts.
|
|
506
|
+
"""
|
|
507
|
+
|
|
508
|
+
|
|
484
509
|
def main(argv: list[str] | None = None) -> int:
|
|
485
510
|
parser = argparse.ArgumentParser(
|
|
511
|
+
prog="okstra context-cost",
|
|
512
|
+
epilog=_CLI_EPILOG,
|
|
513
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
486
514
|
description="Estimate context/file-read cost for an okstra task bundle."
|
|
487
515
|
)
|
|
488
516
|
parser.add_argument("target", help="task root path or task-key")
|
|
@@ -15,8 +15,15 @@ from .contract_graph import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
_CLI_EPILOG = r"""Usage:
|
|
19
|
+
okstra contract-check --root <repo-or-staging-root> [--profile <id> | --operation <id>]
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
18
23
|
def _parser() -> argparse.ArgumentParser:
|
|
19
24
|
parser = argparse.ArgumentParser(
|
|
25
|
+
epilog=_CLI_EPILOG,
|
|
26
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
20
27
|
prog="okstra contract-check",
|
|
21
28
|
description="Validate runtime contract documents and dependencies.",
|
|
22
29
|
)
|
|
@@ -9,7 +9,8 @@ from pathlib import Path, PurePosixPath
|
|
|
9
9
|
import sys
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
-
from okstra_project.
|
|
12
|
+
from okstra_project.slug import slugify
|
|
13
|
+
from okstra_project.state import StateError, parse_task_key
|
|
13
14
|
|
|
14
15
|
from .convergence_engine import (
|
|
15
16
|
ConvergenceContractError,
|
|
@@ -21,6 +22,11 @@ from .convergence_engine import (
|
|
|
21
22
|
validate_final_state,
|
|
22
23
|
validate_working_state,
|
|
23
24
|
)
|
|
25
|
+
from .convergence_critic_prompt import (
|
|
26
|
+
analyser_results,
|
|
27
|
+
covered_index,
|
|
28
|
+
critic_prompt_body,
|
|
29
|
+
)
|
|
24
30
|
from .convergence_migration import (
|
|
25
31
|
SeedDecision,
|
|
26
32
|
archive_state_bytes,
|
|
@@ -63,7 +69,12 @@ class RunArtifactAuthority:
|
|
|
63
69
|
|
|
64
70
|
def validated_run_authority(manifest_path: Path) -> RunArtifactAuthority:
|
|
65
71
|
supplied = Path(manifest_path)
|
|
66
|
-
if not supplied.is_absolute()
|
|
72
|
+
if not supplied.is_absolute():
|
|
73
|
+
# 같은 세션의 `team dispatch`·`agent-prompt materialize`·`convergence
|
|
74
|
+
# prepare-groups` 는 상대경로를 받는다. 이 명령만 거절하면 같은 값이
|
|
75
|
+
# 명령마다 다르게 취급된다. 심링크 없는 절대경로라는 요구는 그대로다.
|
|
76
|
+
supplied = Path.cwd() / supplied
|
|
77
|
+
if supplied.absolute() != supplied.resolve():
|
|
67
78
|
raise ConvergenceContractError("run manifest path is not canonical")
|
|
68
79
|
payload = load_owned_json_object(supplied)
|
|
69
80
|
execution_manifest = None
|
|
@@ -206,7 +217,18 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
|
|
|
206
217
|
"ticketIds": ["TASK-001"],
|
|
207
218
|
"originEvidence": "No rollback query result is present",
|
|
208
219
|
"evidenceArtifacts": [_EVIDENCE_ARTIFACT],
|
|
209
|
-
}
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
# 이미 있는 finding 과 겹치면 다시 쓰지 않고 그 id 를 댄다.
|
|
223
|
+
# 다시 쓴 finding 은 기각에 검증 라운드 하나를 쓰고, 선언된
|
|
224
|
+
# 중복은 하나도 쓰지 않는다.
|
|
225
|
+
"candidateId": "CG-002",
|
|
226
|
+
"summary": "The migration assumption needs a live row",
|
|
227
|
+
"category": "missing",
|
|
228
|
+
"ticketIds": ["TASK-001"],
|
|
229
|
+
"originEvidence": "The same live row F-001 already cites",
|
|
230
|
+
"duplicateOf": "F-001",
|
|
231
|
+
},
|
|
210
232
|
],
|
|
211
233
|
},
|
|
212
234
|
"coverage-batch": {
|
|
@@ -215,11 +237,14 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
|
|
|
215
237
|
"mode": "coverage",
|
|
216
238
|
# The critic's own provider never votes on its gaps, so `provider` here
|
|
217
239
|
# is deliberately absent from `dispatches[]`.
|
|
240
|
+
#
|
|
241
|
+
# 검증 대상 gap 이 하나이므로 배정도 한 명이다 — 로스터 순서 위
|
|
242
|
+
# round-robin 이 G-001 을 claude-worker 에게 준다. codex-worker 는 이
|
|
243
|
+
# 라운드에서 열리지 않는다.
|
|
218
244
|
"provider": "antigravity",
|
|
219
245
|
"modelExecutionValue": "gemini-3.1-pro",
|
|
220
246
|
"dispatches": [
|
|
221
247
|
{"worker": "claude-worker", "status": "completed", "durationMs": 1250},
|
|
222
|
-
{"worker": "codex-worker", "status": "completed", "durationMs": 980},
|
|
223
248
|
],
|
|
224
249
|
"gaps": [
|
|
225
250
|
{
|
|
@@ -235,28 +260,50 @@ _EXAMPLES: dict[str, dict[str, Any]] = {
|
|
|
235
260
|
"disagreeBasis": None,
|
|
236
261
|
"explanation": "The plan cites no rollback verification step",
|
|
237
262
|
},
|
|
238
|
-
"codex-worker": {
|
|
239
|
-
"verdict": "agree",
|
|
240
|
-
"disagreeBasis": None,
|
|
241
|
-
"explanation": "No rollback assertion appears in the stage map",
|
|
242
|
-
},
|
|
243
263
|
},
|
|
244
|
-
}
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
# 선언된 중복은 검증 라운드에 들어가지 않으므로 표가 없다.
|
|
267
|
+
"gapId": "G-002",
|
|
268
|
+
"summary": "The migration assumption needs a live row",
|
|
269
|
+
"category": "missing",
|
|
270
|
+
"ticketIds": ["TASK-001"],
|
|
271
|
+
"originEvidence": "The same live row F-001 already cites",
|
|
272
|
+
"duplicateOf": "F-001",
|
|
273
|
+
"votes": {},
|
|
274
|
+
},
|
|
245
275
|
],
|
|
246
276
|
},
|
|
247
277
|
}
|
|
248
278
|
|
|
249
279
|
|
|
280
|
+
_CLI_EPILOG = r"""Usage:
|
|
281
|
+
okstra convergence seed --groups <path> --work-state <path> \
|
|
282
|
+
--final-state <path> --migration-dir <path> [--restart-from-round0]
|
|
283
|
+
okstra convergence plan-round --work-state <path> --plan <path>
|
|
284
|
+
okstra convergence apply-round --work-state <path> --plan <path> \
|
|
285
|
+
--results <path>
|
|
286
|
+
okstra convergence critic-prompt --run-manifest <path>
|
|
287
|
+
okstra convergence apply-critic-gaps --work-state <path> --results <path>
|
|
288
|
+
okstra convergence finalize --work-state <path> --output <path>
|
|
289
|
+
okstra convergence validate --state <path> --kind <working|final>
|
|
290
|
+
okstra convergence example --kind <groups|round-results|critic-results>
|
|
291
|
+
|
|
292
|
+
Working lifecycle:
|
|
293
|
+
groups v1.0/v2.0 → work v1.0/v2.0 → round-plan/results v1.0 → final v1.3
|
|
294
|
+
historical final readers: v1.0, v1.1, v1.2
|
|
295
|
+
|
|
296
|
+
This internal admin command is consumed by the Phase 5.5 lead contract. It
|
|
297
|
+
does not install or revive the obsolete okstra-convergence skill.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
|
|
250
301
|
def _parser() -> argparse.ArgumentParser:
|
|
251
302
|
parser = argparse.ArgumentParser(
|
|
303
|
+
epilog=_CLI_EPILOG,
|
|
304
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
252
305
|
prog="okstra convergence",
|
|
253
306
|
description="Advance and validate deterministic re-verification state.",
|
|
254
|
-
epilog=(
|
|
255
|
-
"Working lifecycle: groups v1.0/v2.0 → work v1.0/v2.0 → "
|
|
256
|
-
"round-plan/results v1.0 → final v1.3\n"
|
|
257
|
-
"historical final readers: v1.0, v1.1, v1.2"
|
|
258
|
-
),
|
|
259
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
260
307
|
)
|
|
261
308
|
subparsers = parser.add_subparsers(dest="operation", required=True)
|
|
262
309
|
|
|
@@ -323,6 +370,21 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
323
370
|
apply.add_argument("--plan", type=Path, required=True)
|
|
324
371
|
apply.add_argument("--results", type=Path, required=True)
|
|
325
372
|
|
|
373
|
+
critic_prompt = subparsers.add_parser(
|
|
374
|
+
"critic-prompt",
|
|
375
|
+
help="render the coverage-critic task instructions to stdout",
|
|
376
|
+
description=(
|
|
377
|
+
"Print the coverage-critic seed body. The lead writes it verbatim "
|
|
378
|
+
"to the task-instructions file the prompt materializer's "
|
|
379
|
+
"`--instruction` takes — it carries the Round 0 finding list, one "
|
|
380
|
+
"line per Phase 4 analyser result, the prior plan's already-covered "
|
|
381
|
+
"id index when this task has one, and the two mandates. Nothing "
|
|
382
|
+
"here is hand-written."
|
|
383
|
+
),
|
|
384
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
385
|
+
)
|
|
386
|
+
critic_prompt.add_argument("--run-manifest", type=Path, required=True)
|
|
387
|
+
|
|
326
388
|
apply_critic = subparsers.add_parser(
|
|
327
389
|
"apply-critic-gaps",
|
|
328
390
|
help="apply one coverage-critic verification batch",
|
|
@@ -1189,6 +1251,32 @@ def _convergence_config(authority: RunArtifactAuthority) -> Mapping[str, Any]:
|
|
|
1189
1251
|
return config
|
|
1190
1252
|
|
|
1191
1253
|
|
|
1254
|
+
def _critic_prompt(args: argparse.Namespace) -> str:
|
|
1255
|
+
"""coverage-critic 지시문 본문. 입력은 이 run 의 매니페스트 하나뿐이다."""
|
|
1256
|
+
authority = validated_run_authority(args.run_manifest)
|
|
1257
|
+
groups_path = authority.run_dir / "state" / _canonical_run_artifact_name(
|
|
1258
|
+
"convergence-groups", authority.task_type, authority.state_sequence
|
|
1259
|
+
)
|
|
1260
|
+
if not groups_path.is_file():
|
|
1261
|
+
raise ConvergenceContractError(
|
|
1262
|
+
"the critic seed needs the Round 0 grouping; run "
|
|
1263
|
+
f"`okstra convergence prepare-groups` first: {groups_path}"
|
|
1264
|
+
)
|
|
1265
|
+
groups = load_owned_json_object(groups_path)
|
|
1266
|
+
return critic_prompt_body(
|
|
1267
|
+
groups,
|
|
1268
|
+
analysis_packet_path=_manifest_authority_string(
|
|
1269
|
+
authority.payload, "analysisPacketPath"
|
|
1270
|
+
),
|
|
1271
|
+
results=analyser_results(
|
|
1272
|
+
groups,
|
|
1273
|
+
project_root=authority.project_root,
|
|
1274
|
+
run_dir=authority.run_dir,
|
|
1275
|
+
),
|
|
1276
|
+
covered=covered_index(authority.project_root, authority.run_dir),
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
|
|
1192
1280
|
def _prepare_groups(args: argparse.Namespace) -> tuple[str, Path]:
|
|
1193
1281
|
authority = validated_run_authority(args.run_manifest)
|
|
1194
1282
|
manifest = authority.payload
|
|
@@ -1253,6 +1341,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1253
1341
|
)
|
|
1254
1342
|
return 0
|
|
1255
1343
|
try:
|
|
1344
|
+
if args.operation == "critic-prompt":
|
|
1345
|
+
print(_critic_prompt(args), end="")
|
|
1346
|
+
return 0
|
|
1256
1347
|
action, path = _execute(args)
|
|
1257
1348
|
except (ConvergenceContractError, VerdictBlockError,
|
|
1258
1349
|
json.JSONDecodeError, ValueError) as exc:
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""Coverage-critic 시드 프롬프트를 디스크의 사실에서 결정적으로 렌더한다.
|
|
2
|
+
|
|
3
|
+
Phase 5.5 의 critic 은 리드가 손으로 쓴 지시문을 받아 왔다. 시드가 Round 0
|
|
4
|
+
finding 목록뿐이라 critic 은 "이미 다뤄진 것"을 알 방법이 없었고, 직전 리포트가
|
|
5
|
+
이미 덮은 항목을 갭으로 다시 제안했다. 그 재제안 한 건의 값은 gap 검증 라운드
|
|
6
|
+
하나이고, 그 라운드는 직렬 경로 위에 있다.
|
|
7
|
+
|
|
8
|
+
이 모듈은 그 시드를 코드로 만든다. 네 덩어리를 싣는다.
|
|
9
|
+
|
|
10
|
+
- Round 0 통합 finding 목록 (리드가 붙여 넣던 것과 같은 내용).
|
|
11
|
+
- Phase 4 분석자별 결과 파일 경로와 그 워커가 낸 finding id — "이름을 댄 결과를
|
|
12
|
+
열어라"는 지시가 실제 파일을 가리키게 한다.
|
|
13
|
+
- 직전 implementation-planning 리포트가 있으면(재실행) 그 리포트의 커버리지 행
|
|
14
|
+
id, clarification 행 id, stage 제목만 뽑은 "이미 다뤄진 것" 색인. **본문은
|
|
15
|
+
절대 싣지 않는다** — 283K 리포트를 통째로 실었다가 report-writer 가 timeout
|
|
16
|
+
난 적이 있다.
|
|
17
|
+
- 두 갈래 지시문(누락 / `unrequested-scope`)과 중복 선언 규칙.
|
|
18
|
+
|
|
19
|
+
산출물은 프롬프트 materializer 의 `--instruction` 이 받는 task 지시문 파일
|
|
20
|
+
본문이다(`prompts/lead/convergence.md` §"Dispatch (fresh one-shot)"). 그래서 그
|
|
21
|
+
계약이 BLOCKING 으로 요구하는 두 줄(`**Prompt Delivery Mode:**`, `## Inputs`
|
|
22
|
+
아래의 Primary analysis packet)도 이 본문이 직접 낸다 — 리드가 손으로 덧붙이던
|
|
23
|
+
자리다.
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any, Mapping, Sequence
|
|
30
|
+
|
|
31
|
+
from .convergence_provenance import worker_result_suffix
|
|
32
|
+
from .fixed_text import line
|
|
33
|
+
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
34
|
+
from .paths import RunRef
|
|
35
|
+
from .plan_run_root import list_implementation_planning_reports
|
|
36
|
+
from .stage_map import StageMapError, parse_stage_map_file
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
PROMPT_DELIVERY_MODE = "eager-include"
|
|
40
|
+
PLANNING_TASK_TYPE = "implementation-planning"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CriticPromptError(ValueError):
|
|
44
|
+
"""시드를 결정적으로 만들 수 없다."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class AnalyserResult:
|
|
49
|
+
"""한 Phase 4 분석자가 남긴 결과 파일과 그 워커가 낸 finding id."""
|
|
50
|
+
|
|
51
|
+
worker_id: str
|
|
52
|
+
result_path: str
|
|
53
|
+
finding_ids: tuple[str, ...]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class CoveredIndex:
|
|
58
|
+
"""직전 계획이 이미 덮은 것의 id 색인. 본문은 담지 않는다.
|
|
59
|
+
|
|
60
|
+
``stage_titles`` 의 ``None`` 은 "Stage Map 을 못 읽었다" 이고 빈 튜플과 다르다
|
|
61
|
+
— 계획의 stage 번호는 `1..N` 이라 0 개인 계획은 없으므로, 못 읽은 것을 0 으로
|
|
62
|
+
쓰면 critic 에게 거짓을 말하게 된다(`stage_ledger.build_stage_ledger` 가 같은
|
|
63
|
+
이유로 세 결과를 구분한다).
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
plan_path: str
|
|
67
|
+
requirement_ids: tuple[str, ...]
|
|
68
|
+
clarification_ids: tuple[str, ...]
|
|
69
|
+
stage_titles: tuple[tuple[int, str], ...] | None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _nonempty_string(value: Any) -> str:
|
|
73
|
+
return value if isinstance(value, str) and value.strip() else ""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _project_relative(project_root: Path, path: Path) -> str:
|
|
77
|
+
try:
|
|
78
|
+
return path.resolve().relative_to(Path(project_root).resolve()).as_posix()
|
|
79
|
+
except ValueError:
|
|
80
|
+
# expected-miss: 프로젝트 루트 밖의 경로 = 상대화 대상이 아니다. run 트리
|
|
81
|
+
# 밖을 가리키는 것은 정상 분기이며, 그때는 있는 그대로 적는다.
|
|
82
|
+
return path.as_posix()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _analysis_roster(groups: Mapping[str, Any]) -> list[str]:
|
|
86
|
+
workers = groups.get("workers")
|
|
87
|
+
if not isinstance(workers, list):
|
|
88
|
+
return []
|
|
89
|
+
return [
|
|
90
|
+
worker["workerId"]
|
|
91
|
+
for worker in workers
|
|
92
|
+
if isinstance(worker, Mapping)
|
|
93
|
+
and worker.get("audience") == "analysis"
|
|
94
|
+
and _nonempty_string(worker.get("workerId"))
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _findings_by_worker(groups: Mapping[str, Any]) -> dict[str, list[str]]:
|
|
99
|
+
"""워커 → 그 워커가 출처인 finding id. 그룹 순서를 유지한다.
|
|
100
|
+
|
|
101
|
+
`sourceItems` 와 `discoveredBy` 를 합집합으로 본다 — 그룹 파서
|
|
102
|
+
(`convergence_engine._parse_group`)가 출처 워커를 그렇게 센다. 한쪽만 보면
|
|
103
|
+
다른 쪽에만 적힌 워커의 결과 파일이 "낸 것 없음"으로 보인다.
|
|
104
|
+
"""
|
|
105
|
+
by_worker: dict[str, list[str]] = {}
|
|
106
|
+
|
|
107
|
+
def _record(worker: Any, finding_id: str) -> None:
|
|
108
|
+
name = _nonempty_string(worker)
|
|
109
|
+
if name and finding_id not in by_worker.setdefault(name, []):
|
|
110
|
+
by_worker[name].append(finding_id)
|
|
111
|
+
|
|
112
|
+
for group in groups.get("groups") or []:
|
|
113
|
+
if not isinstance(group, Mapping):
|
|
114
|
+
continue
|
|
115
|
+
finding_id = _nonempty_string(group.get("findingId"))
|
|
116
|
+
if not finding_id:
|
|
117
|
+
continue
|
|
118
|
+
for item in group.get("sourceItems") or []:
|
|
119
|
+
source = item.get("worker") if isinstance(item, Mapping) else None
|
|
120
|
+
_record(source, finding_id)
|
|
121
|
+
discovered = group.get("discoveredBy")
|
|
122
|
+
for worker in discovered if isinstance(discovered, Mapping) else {}:
|
|
123
|
+
_record(worker, finding_id)
|
|
124
|
+
return by_worker
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def analyser_results(
|
|
128
|
+
groups: Mapping[str, Any],
|
|
129
|
+
*,
|
|
130
|
+
project_root: Path,
|
|
131
|
+
run_dir: Path,
|
|
132
|
+
) -> list[AnalyserResult]:
|
|
133
|
+
"""분석 audience 워커마다 결과 파일 경로와 finding id 를 짝지어 돌려준다.
|
|
134
|
+
|
|
135
|
+
결과 파일명은 `convergence_provenance` 가 이미 소유한 규칙을 그대로 쓴다 —
|
|
136
|
+
`<worker>-worker-<task-type>-<workerResults seq>.md`. state 시퀀스와 다른
|
|
137
|
+
카운터라 그룹 파일명에서 접미사를 빌려 오면 빗나간다.
|
|
138
|
+
"""
|
|
139
|
+
suffix = worker_result_suffix(Path(run_dir), groups)
|
|
140
|
+
if suffix is None:
|
|
141
|
+
raise CriticPromptError(
|
|
142
|
+
"cannot resolve the worker-result suffix from the grouping's "
|
|
143
|
+
"runManifestPath"
|
|
144
|
+
)
|
|
145
|
+
results_dir = Path(run_dir) / "worker-results"
|
|
146
|
+
by_worker = _findings_by_worker(groups)
|
|
147
|
+
return [
|
|
148
|
+
AnalyserResult(
|
|
149
|
+
worker_id=worker,
|
|
150
|
+
result_path=_project_relative(
|
|
151
|
+
project_root, results_dir / f"{worker}-worker-{suffix}.md"
|
|
152
|
+
),
|
|
153
|
+
finding_ids=tuple(by_worker.get(worker, ())),
|
|
154
|
+
)
|
|
155
|
+
for worker in _analysis_roster(groups)
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def covered_index(project_root: Path, run_dir: Path) -> CoveredIndex | None:
|
|
160
|
+
"""직전 implementation-planning 리포트의 id 색인. 없으면 ``None``.
|
|
161
|
+
|
|
162
|
+
Phase 5.5 는 Phase 6 보다 앞이라 이 run 의 리포트는 아직 없다. 그래서
|
|
163
|
+
reports 디렉터리의 최신 리포트는 언제나 **직전** run 의 것이다.
|
|
164
|
+
|
|
165
|
+
계획 run 에서만 싣는다. 다른 phase 의 finding 은 계획의 커버리지 행이 덮는
|
|
166
|
+
대상이 아니라서, 같은 목록을 "이미 다뤄진 것"으로 보여 주면 critic 이 실재하는
|
|
167
|
+
갭을 중복으로 접는다.
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
ref = RunRef.from_run_dir(Path(run_dir))
|
|
171
|
+
except ValueError:
|
|
172
|
+
# expected-miss: `runs/` 앵커가 없는 경로 = okstra run 트리가 아니다.
|
|
173
|
+
# 그런 곳에는 직전 계획도 없다.
|
|
174
|
+
return None
|
|
175
|
+
if ref.task_type != PLANNING_TASK_TYPE:
|
|
176
|
+
return None
|
|
177
|
+
reports_dir = RunRef.from_task_root(
|
|
178
|
+
ref.task_root, PLANNING_TASK_TYPE
|
|
179
|
+
).reports_dir
|
|
180
|
+
if not reports_dir.is_dir():
|
|
181
|
+
return None
|
|
182
|
+
reports = list_implementation_planning_reports(reports_dir)
|
|
183
|
+
if not reports:
|
|
184
|
+
return None
|
|
185
|
+
report = reports[0]
|
|
186
|
+
try:
|
|
187
|
+
data = load_owned_object(report, artifact="planning final report")
|
|
188
|
+
except (OSError, UnicodeError, JsonBoundaryError) as exc:
|
|
189
|
+
raise CriticPromptError(
|
|
190
|
+
f"prior planning report is unreadable: {report} ({exc})"
|
|
191
|
+
) from exc
|
|
192
|
+
if not isinstance(data, Mapping):
|
|
193
|
+
raise CriticPromptError(f"prior planning report is not an object: {report}")
|
|
194
|
+
planning = data.get("implementationPlanning")
|
|
195
|
+
planning = planning if isinstance(planning, Mapping) else {}
|
|
196
|
+
try:
|
|
197
|
+
stages: list[Any] | None = parse_stage_map_file(report)
|
|
198
|
+
except StageMapError:
|
|
199
|
+
# expected-miss: schema v1 마크다운 계획이거나 Stage Map 이 깨졌다. 둘 다
|
|
200
|
+
# 나머지 색인(요구사항·clarification id)은 그대로 쓸 수 있는 분기이므로,
|
|
201
|
+
# 여기서 멈추는 대신 "못 읽었다"를 그대로 실어 보낸다.
|
|
202
|
+
stages = None
|
|
203
|
+
return CoveredIndex(
|
|
204
|
+
plan_path=_project_relative(project_root, report),
|
|
205
|
+
requirement_ids=tuple(
|
|
206
|
+
_nonempty_string(row.get("id"))
|
|
207
|
+
for row in planning.get("requirementCoverage") or []
|
|
208
|
+
if isinstance(row, Mapping) and _nonempty_string(row.get("id"))
|
|
209
|
+
),
|
|
210
|
+
clarification_ids=tuple(
|
|
211
|
+
_nonempty_string(row.get("id"))
|
|
212
|
+
for row in data.get("clarificationItems") or []
|
|
213
|
+
if isinstance(row, Mapping) and _nonempty_string(row.get("id"))
|
|
214
|
+
),
|
|
215
|
+
stage_titles=None if stages is None else tuple(
|
|
216
|
+
(stage.stage_number, stage.title) for stage in stages
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
_MANDATES = """
|
|
222
|
+
Your job has exactly two halves. Answer both.
|
|
223
|
+
|
|
224
|
+
(1) MISSING — name what nobody covered:
|
|
225
|
+
- files / directories / execution paths nobody inspected,
|
|
226
|
+
- requirements or acceptance points with zero findings,
|
|
227
|
+
- claims raised but never verified.
|
|
228
|
+
For each, emit a NEW finding with evidence (file:line or the requirement quote).
|
|
229
|
+
|
|
230
|
+
(2) UNREQUESTED — name work these findings propose that no requirement asked for:
|
|
231
|
+
- a finding whose proposed change serves no requirement, scope item, or
|
|
232
|
+
acceptance point you can QUOTE from the analysis packet,
|
|
233
|
+
- an abstraction, configuration knob, or generalization proposed for a caller or
|
|
234
|
+
a case nobody has stated,
|
|
235
|
+
- a rewrite, migration, or cleanup of code the requirements never mention.
|
|
236
|
+
For each, emit a candidate with `category: "unrequested-scope"`, quote the
|
|
237
|
+
proposed work verbatim, and state which requirement you searched for and did not
|
|
238
|
+
find.
|
|
239
|
+
|
|
240
|
+
Do NOT restate an existing finding. An overlap with an existing finding is
|
|
241
|
+
declared as `duplicateOf` naming that finding id, not restated as a new
|
|
242
|
+
candidate — a restated finding costs a whole verification round to reject, while
|
|
243
|
+
a declared duplicate costs none. Judge (2) against the analysis packet's
|
|
244
|
+
requirements and scope, never against your own preference for how the code
|
|
245
|
+
should look — "I would have done it differently" is not unrequested work, and
|
|
246
|
+
neither is work the packet authorizes but you consider unnecessary. If a half
|
|
247
|
+
has nothing, say so explicitly for that half; silence on one half is an
|
|
248
|
+
incomplete result.
|
|
249
|
+
|
|
250
|
+
Read before proposing anything: the analysis packet named above for requirements
|
|
251
|
+
and phase scope, the Round 0 findings below, and the named result file of any
|
|
252
|
+
analyser whose coverage you intend to call incomplete. Do NOT infer missing
|
|
253
|
+
verification from a one-line summary.
|
|
254
|
+
""".strip()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def critic_prompt_body(
|
|
258
|
+
groups: Mapping[str, Any],
|
|
259
|
+
*,
|
|
260
|
+
analysis_packet_path: str,
|
|
261
|
+
results: Sequence[AnalyserResult],
|
|
262
|
+
covered: CoveredIndex | None,
|
|
263
|
+
) -> str:
|
|
264
|
+
"""critic 지시문 파일 본문. 같은 입력이면 같은 바이트를 낸다."""
|
|
265
|
+
task_key = _nonempty_string(groups.get("taskKey"))
|
|
266
|
+
if not task_key:
|
|
267
|
+
raise CriticPromptError("convergence groups carry no taskKey")
|
|
268
|
+
packet = _nonempty_string(analysis_packet_path)
|
|
269
|
+
if not packet.endswith("analysis-packet.md"):
|
|
270
|
+
raise CriticPromptError(
|
|
271
|
+
"run manifest analysisPacketPath must name analysis-packet.md; "
|
|
272
|
+
f"found `{analysis_packet_path}`"
|
|
273
|
+
)
|
|
274
|
+
rows: list[str] = [
|
|
275
|
+
f"**Prompt Delivery Mode:** {PROMPT_DELIVERY_MODE}\n",
|
|
276
|
+
"\n",
|
|
277
|
+
f"# Coverage critic — {task_key}\n",
|
|
278
|
+
"\n",
|
|
279
|
+
"## Inputs\n",
|
|
280
|
+
f"- Primary analysis packet: `{packet}`\n",
|
|
281
|
+
"\n",
|
|
282
|
+
"## Mandate\n",
|
|
283
|
+
"\n",
|
|
284
|
+
_MANDATES,
|
|
285
|
+
"\n\n",
|
|
286
|
+
]
|
|
287
|
+
rows.extend(_round0_rows(groups))
|
|
288
|
+
rows.extend(_analyser_rows(results))
|
|
289
|
+
rows.extend(_covered_rows(covered))
|
|
290
|
+
return "".join(rows)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _round0_rows(groups: Mapping[str, Any]) -> list[str]:
|
|
294
|
+
findings = [
|
|
295
|
+
group for group in groups.get("groups") or [] if isinstance(group, Mapping)
|
|
296
|
+
]
|
|
297
|
+
rows = ["## Round 0 consolidated findings\n", line("Finding count", len(findings))]
|
|
298
|
+
for group in findings:
|
|
299
|
+
rows.append(f"\n### {_nonempty_string(group.get('findingId')) or '-'}\n")
|
|
300
|
+
rows.append(line("Summary", group.get("summary")))
|
|
301
|
+
rows.append(line("Category", group.get("category")))
|
|
302
|
+
rows.append(line("Ticket ids", ", ".join(group.get("ticketIds") or [])))
|
|
303
|
+
rows.append(line("Origin worker", group.get("originWorker")))
|
|
304
|
+
rows.append(line("Origin evidence", group.get("originEvidence")))
|
|
305
|
+
rows.append("\n")
|
|
306
|
+
return rows
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _analyser_rows(results: Sequence[AnalyserResult]) -> list[str]:
|
|
310
|
+
rows = [
|
|
311
|
+
"## Phase 4 analyser results\n",
|
|
312
|
+
line("Analyser count", len(results)),
|
|
313
|
+
]
|
|
314
|
+
for index, result in enumerate(results, 1):
|
|
315
|
+
rows.append(line(f"Analyser {index} worker id", result.worker_id))
|
|
316
|
+
rows.append(line(f"Analyser {index} result path", result.result_path))
|
|
317
|
+
rows.append(
|
|
318
|
+
line(f"Analyser {index} finding ids", ", ".join(result.finding_ids))
|
|
319
|
+
)
|
|
320
|
+
rows.append("\n")
|
|
321
|
+
return rows
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _covered_rows(covered: CoveredIndex | None) -> list[str]:
|
|
325
|
+
if covered is None:
|
|
326
|
+
return []
|
|
327
|
+
rows = [
|
|
328
|
+
"## Already covered by the prior plan\n",
|
|
329
|
+
"\n",
|
|
330
|
+
"Ids and titles only. A candidate that repeats one of these is a "
|
|
331
|
+
"duplicate: declare it with `duplicateOf`, or do not raise it.\n",
|
|
332
|
+
"\n",
|
|
333
|
+
line("Prior plan path", covered.plan_path),
|
|
334
|
+
line("Requirement coverage row ids", ", ".join(covered.requirement_ids)),
|
|
335
|
+
line("Clarification row ids", ", ".join(covered.clarification_ids)),
|
|
336
|
+
]
|
|
337
|
+
if covered.stage_titles is None:
|
|
338
|
+
rows.append(line("Stage map", "unreadable"))
|
|
339
|
+
rows.append("\n")
|
|
340
|
+
return rows
|
|
341
|
+
rows.append(line("Stage count", len(covered.stage_titles)))
|
|
342
|
+
for number, title in covered.stage_titles:
|
|
343
|
+
rows.append(line(f"Stage {number} title", title))
|
|
344
|
+
rows.append("\n")
|
|
345
|
+
return rows
|