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,135 @@
|
|
|
1
|
+
"""처분(disposition)의 어휘와 그것이 진행을 막는지의 판정.
|
|
2
|
+
|
|
3
|
+
`Blocks` 값과 답변 처분만 보고 결정한다 — 파일도, 표도 열지 않는다. 게이트를
|
|
4
|
+
읽는 쪽(`.scan`)과 리포트를 읽는 쪽(`.rows`)이 같은 정의를 쓰게 하는 것이
|
|
5
|
+
이 층이 따로 있는 이유다.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Mapping
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
APPROVAL_BLOCKS = frozenset({"approval"})
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
USER_INPUT_BLOCKS = frozenset({"approval", "next-phase"})
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
PROCEEDING_DISPOSITIONS = frozenset({"answer", "select", "accept-risk"})
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
RETURN_DISPOSITIONS = frozenset({"request-revision", "reject"})
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
INCORPORATED_LEDGER_DISPOSITIONS = frozenset(
|
|
25
|
+
{"superseded", "no-dependent-statement"}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def clarification_disposition(row: Mapping[str, object]) -> str:
|
|
30
|
+
"""행에 기록된 사용자 처분. 없으면 빈 문자열."""
|
|
31
|
+
resolution = row.get("resolution")
|
|
32
|
+
if not isinstance(resolution, Mapping):
|
|
33
|
+
context = row.get("approvalContext")
|
|
34
|
+
if isinstance(context, Mapping):
|
|
35
|
+
resolution = context.get("resolution")
|
|
36
|
+
if isinstance(resolution, Mapping):
|
|
37
|
+
disposition = str(resolution.get("disposition") or "").strip()
|
|
38
|
+
if disposition:
|
|
39
|
+
return disposition
|
|
40
|
+
user_input = str(row.get("userInput") or "").strip()
|
|
41
|
+
options = row.get("options")
|
|
42
|
+
if user_input and isinstance(options, list):
|
|
43
|
+
for option in options:
|
|
44
|
+
if (
|
|
45
|
+
isinstance(option, Mapping)
|
|
46
|
+
and str(option.get("answer") or "").strip() == user_input
|
|
47
|
+
):
|
|
48
|
+
return str(option.get("disposition") or "").strip()
|
|
49
|
+
return ""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def incorporated_clarification_ids(
|
|
53
|
+
report_data: Mapping[str, object] | None,
|
|
54
|
+
) -> frozenset[str]:
|
|
55
|
+
"""이 런이 답을 본문에 반영했다고 원장에 적은 C-id.
|
|
56
|
+
|
|
57
|
+
`superseded` 와 `no-dependent-statement` 만 센다. 원장에 없는
|
|
58
|
+
`request-revision` / `reject` 는 아직 다음 계획을 막는 되돌림이다.
|
|
59
|
+
"""
|
|
60
|
+
if not isinstance(report_data, Mapping):
|
|
61
|
+
return frozenset()
|
|
62
|
+
planning = report_data.get("implementationPlanning")
|
|
63
|
+
if not isinstance(planning, Mapping):
|
|
64
|
+
return frozenset()
|
|
65
|
+
ledger = planning.get("supersessionLedger")
|
|
66
|
+
if not isinstance(ledger, list):
|
|
67
|
+
return frozenset()
|
|
68
|
+
ids: set[str] = set()
|
|
69
|
+
for entry in ledger:
|
|
70
|
+
if not isinstance(entry, Mapping):
|
|
71
|
+
continue
|
|
72
|
+
row_id = str(entry.get("clarificationId") or "").strip()
|
|
73
|
+
disposition = str(entry.get("disposition") or "").strip().lower()
|
|
74
|
+
if row_id and disposition in INCORPORATED_LEDGER_DISPOSITIONS:
|
|
75
|
+
ids.add(row_id)
|
|
76
|
+
return frozenset(ids)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def row_blocks_progress(
|
|
80
|
+
status: str,
|
|
81
|
+
disposition: str = "",
|
|
82
|
+
*,
|
|
83
|
+
incorporated: bool = False,
|
|
84
|
+
) -> bool:
|
|
85
|
+
"""이 행이 승인·다음 단계 진입을 막는가.
|
|
86
|
+
|
|
87
|
+
진행 처분(`accept-risk` / `select` / `answer`)은 고치지 않은 DISAGREE 를
|
|
88
|
+
행과 투표에 남긴 채로 게이트만 내린다. `request-revision` / `reject` 는
|
|
89
|
+
사용자가 진행을 거절한 것이므로 막는다. 다만 이 보고서 원장이 그 답을
|
|
90
|
+
이미 반영했으면(`incorporated`) 같은 되돌림이 다음 계획 런을 강제하지
|
|
91
|
+
않는다. 처분이 없는 `answered` 도 판단 기록이므로 막지 않는다.
|
|
92
|
+
"""
|
|
93
|
+
normalized_status = status.strip().lower()
|
|
94
|
+
normalized_disposition = disposition.strip().lower()
|
|
95
|
+
if normalized_status == "obsolete":
|
|
96
|
+
return False
|
|
97
|
+
if normalized_disposition in PROCEEDING_DISPOSITIONS:
|
|
98
|
+
return False
|
|
99
|
+
if normalized_disposition in RETURN_DISPOSITIONS:
|
|
100
|
+
return not incorporated
|
|
101
|
+
return normalized_status not in {"answered", "resolved"}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def progress_blocking_ids(
|
|
105
|
+
rows: object,
|
|
106
|
+
blocking_values: frozenset[str] = APPROVAL_BLOCKS,
|
|
107
|
+
*,
|
|
108
|
+
report_data: Mapping[str, object] | None = None,
|
|
109
|
+
) -> list[str]:
|
|
110
|
+
"""게이트를 아직 막는 행 id. 사용자 진행 처분이 있는 행은 빠진다.
|
|
111
|
+
|
|
112
|
+
``report_data`` 가 있으면 원장에 반영된 되돌림 행도 빠진다.
|
|
113
|
+
"""
|
|
114
|
+
if not isinstance(rows, list):
|
|
115
|
+
return []
|
|
116
|
+
incorporated = incorporated_clarification_ids(report_data)
|
|
117
|
+
ids: list[str] = []
|
|
118
|
+
for row in rows:
|
|
119
|
+
if not isinstance(row, Mapping):
|
|
120
|
+
continue
|
|
121
|
+
blocks = str(row.get("blocks") or "").strip().lower()
|
|
122
|
+
status = str(row.get("status") or "").strip()
|
|
123
|
+
row_id = row.get("id")
|
|
124
|
+
if (
|
|
125
|
+
blocks in blocking_values
|
|
126
|
+
and row_blocks_progress(
|
|
127
|
+
status,
|
|
128
|
+
clarification_disposition(row),
|
|
129
|
+
incorporated=isinstance(row_id, str) and row_id in incorporated,
|
|
130
|
+
)
|
|
131
|
+
and isinstance(row_id, str)
|
|
132
|
+
and row_id
|
|
133
|
+
):
|
|
134
|
+
ids.append(row_id)
|
|
135
|
+
return ids
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""schema-v1 의 `## 1. Clarification Items` 표를 읽는다.
|
|
2
|
+
|
|
3
|
+
행 하나가 `ClarificationItem` 이 되고, 그 표를 찾고 걷고 메타 셀을 푸는 것이
|
|
4
|
+
여기 전부다. 판정(무엇이 진행을 막는가)은 `.dispositions`, 파일을 여는 것은
|
|
5
|
+
`.rows` 다.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ..md_table import is_separator_row, split_pipe_row, to_cell_text
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Optional
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
SECTION_HEADING_PATTERN = re.compile(
|
|
16
|
+
r'^##\s+1\.\s+Clarification Items\s*(?:<a id="[^"]+"></a>\s*)?$',
|
|
17
|
+
re.MULTILINE,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
NEXT_TOP_LEVEL_HEADING_PATTERN = re.compile(r"^##\s+(?!1\.)", re.MULTILINE)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class ClarificationItem:
|
|
26
|
+
"""One row of the §1 table.
|
|
27
|
+
|
|
28
|
+
``raw_*`` fields preserve the exact cell text (after backtick stripping)
|
|
29
|
+
for diagnostics; canonical lowercased versions live in ``blocks`` /
|
|
30
|
+
``status`` for predicate use.
|
|
31
|
+
"""
|
|
32
|
+
row_id: str
|
|
33
|
+
kind: str # "material" | "decision" | "data-point" | other
|
|
34
|
+
blocks: str # canonical lowercase: "approval" | "next-phase" | "none" | other
|
|
35
|
+
status: str # canonical lowercase: "open" | "answered" | "resolved" | "obsolete" | other
|
|
36
|
+
raw_blocks: str
|
|
37
|
+
raw_status: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_CELL_ANCHOR_RE = re.compile(r'<a id="([^"]*)"></a>')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _strip_backticks(cell: str) -> str:
|
|
44
|
+
s = _CELL_ANCHOR_RE.sub("", cell.strip()).strip()
|
|
45
|
+
if s.startswith("`") and s.endswith("`") and len(s) >= 2:
|
|
46
|
+
s = s[1:-1].strip()
|
|
47
|
+
return s
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _split_pipe_row(line: str) -> list[str]:
|
|
51
|
+
"""``md_table.split_pipe_row`` + §1-specific cell normalization
|
|
52
|
+
(scroll-anchor removal, outer-backtick unwrap)."""
|
|
53
|
+
return [_strip_backticks(c) for c in split_pipe_row(line)]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _section_1_slice(report_text: str) -> Optional[str]:
|
|
57
|
+
"""Return the substring spanning the §1 section (heading exclusive of the
|
|
58
|
+
next ``##`` heading), or None if §1 is absent."""
|
|
59
|
+
start_match = SECTION_HEADING_PATTERN.search(report_text)
|
|
60
|
+
if not start_match:
|
|
61
|
+
return None
|
|
62
|
+
rest = report_text[start_match.end():]
|
|
63
|
+
end_match = NEXT_TOP_LEVEL_HEADING_PATTERN.search(rest)
|
|
64
|
+
return rest[: end_match.start()] if end_match else rest
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
_META_ID_RE = re.compile(r"([A-Za-z][A-Za-z0-9]*-\d+)")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _meta_field(cell: str, key: str) -> str:
|
|
71
|
+
"""Extract a ``<key>: value`` field from a stacked §1 meta cell.
|
|
72
|
+
|
|
73
|
+
The §1 table collapses the short columns into one ``<br>``-delimited
|
|
74
|
+
metadata cell (``**C-101**<br>Ticket: `DEV-1`<br>Kind: `decision`<br>
|
|
75
|
+
Blocks: `approval`<br>Status: open``). Values may be backtick-wrapped.
|
|
76
|
+
"""
|
|
77
|
+
m = re.search(
|
|
78
|
+
rf"{re.escape(key)}:\s*`?\s*([^`<|]+?)\s*`?\s*(?:<br\s*/?>|$)",
|
|
79
|
+
cell,
|
|
80
|
+
re.IGNORECASE,
|
|
81
|
+
)
|
|
82
|
+
return m.group(1).strip() if m else ""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _meta_id(cell: str) -> str:
|
|
86
|
+
"""The bold headline ID of a §1 meta cell (the part before the first <br>)."""
|
|
87
|
+
headline = re.split(r"<br\s*/?>", cell, maxsplit=1)[0]
|
|
88
|
+
m = _META_ID_RE.search(headline)
|
|
89
|
+
return m.group(1) if m else ""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def parse_meta_cell(cell: str) -> Optional[ClarificationItem]:
|
|
93
|
+
"""Parse one §1 stacked meta cell into a ``ClarificationItem``, or ``None``
|
|
94
|
+
when the cell is not a §1 meta cell (no ``Blocks:``/``Status:`` markers —
|
|
95
|
+
e.g. a header or an unrelated table). Shared by the approval-gate parser
|
|
96
|
+
and the HTML view's form-attach pass so both read the cell identically.
|
|
97
|
+
"""
|
|
98
|
+
raw_blocks = _meta_field(cell, "Blocks")
|
|
99
|
+
raw_status = _meta_field(cell, "Status")
|
|
100
|
+
if not (raw_blocks and raw_status):
|
|
101
|
+
return None
|
|
102
|
+
return ClarificationItem(
|
|
103
|
+
row_id=_meta_id(cell),
|
|
104
|
+
kind=_meta_field(cell, "Kind").lower(),
|
|
105
|
+
blocks=raw_blocks.lower(),
|
|
106
|
+
status=raw_status.lower(),
|
|
107
|
+
raw_blocks=raw_blocks,
|
|
108
|
+
raw_status=raw_status,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass(frozen=True)
|
|
113
|
+
class _Section1Table:
|
|
114
|
+
"""Outcome of walking the §1 slice for its data table.
|
|
115
|
+
|
|
116
|
+
``items`` is ``None`` when no recognizable table header exists among the
|
|
117
|
+
pipe lines. ``unparsed_row_count`` counts body rows whose metadata cell
|
|
118
|
+
failed ``parse_meta_cell`` (all-empty filler rows excluded).
|
|
119
|
+
``has_pipe_lines`` distinguishes the renderer's legitimate table-less
|
|
120
|
+
placeholder (emptyState bullet) from a table whose header drifted.
|
|
121
|
+
"""
|
|
122
|
+
items: Optional[list[ClarificationItem]]
|
|
123
|
+
unparsed_row_count: int
|
|
124
|
+
has_pipe_lines: bool
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _walk_section_1_table(section: str) -> _Section1Table:
|
|
128
|
+
lines = section.splitlines()
|
|
129
|
+
has_pipe_lines = any(line.lstrip().startswith("|") for line in lines)
|
|
130
|
+
# Locate the §1 data table by its header. The merged-meta layout collapses
|
|
131
|
+
# ID/Ticket/Kind/Blocks/Status into one metadata cell and keeps the
|
|
132
|
+
# English `Statement` + `User input` columns; detect on those two (any
|
|
133
|
+
# other table — intro, legacy 5.1/5.2 — is rejected).
|
|
134
|
+
header_idx = -1
|
|
135
|
+
for idx, line in enumerate(lines):
|
|
136
|
+
if not line.lstrip().startswith("|"):
|
|
137
|
+
continue
|
|
138
|
+
cells = [c.lower() for c in _split_pipe_row(line)]
|
|
139
|
+
if "user input" in cells and any(c.startswith("statement") for c in cells):
|
|
140
|
+
header_idx = idx
|
|
141
|
+
break
|
|
142
|
+
if header_idx < 0:
|
|
143
|
+
return _Section1Table(None, 0, has_pipe_lines)
|
|
144
|
+
|
|
145
|
+
items: list[ClarificationItem] = []
|
|
146
|
+
unparsed = 0
|
|
147
|
+
body_started = False
|
|
148
|
+
for line in lines[header_idx + 1:]:
|
|
149
|
+
if not line.lstrip().startswith("|"):
|
|
150
|
+
if body_started:
|
|
151
|
+
break
|
|
152
|
+
continue
|
|
153
|
+
if is_separator_row(line):
|
|
154
|
+
body_started = True
|
|
155
|
+
continue
|
|
156
|
+
if not body_started:
|
|
157
|
+
continue
|
|
158
|
+
cells = _split_pipe_row(line)
|
|
159
|
+
if not any(cells):
|
|
160
|
+
continue
|
|
161
|
+
item = parse_meta_cell(cells[0])
|
|
162
|
+
if item is None:
|
|
163
|
+
unparsed += 1
|
|
164
|
+
continue
|
|
165
|
+
items.append(item)
|
|
166
|
+
return _Section1Table(items, unparsed, True)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def parse_clarification_items(report_text: str) -> Optional[list[ClarificationItem]]:
|
|
170
|
+
"""Return the list of §1 rows. ``None`` means "no §1 meta table detected"
|
|
171
|
+
(missing section or unrecognized table header) — caller must NOT treat
|
|
172
|
+
that as "table is empty".
|
|
173
|
+
|
|
174
|
+
Lenient view-renderer contract: rows whose metadata cell fails to parse
|
|
175
|
+
are skipped, not surfaced. The approval gate must use
|
|
176
|
+
``scan_approval_gate`` instead, which fail-closes on those rows.
|
|
177
|
+
"""
|
|
178
|
+
section = _section_1_slice(report_text)
|
|
179
|
+
if section is None:
|
|
180
|
+
return None
|
|
181
|
+
return _walk_section_1_table(section).items
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def parse_section_1_rows(report_text: str) -> list[dict]:
|
|
185
|
+
"""§1 테이블 한 행마다 메타(ClarificationItem) + 원문 Statement / Expected
|
|
186
|
+
form 셀.
|
|
187
|
+
|
|
188
|
+
입력은 §1 을 담은 마크다운 본문이다 — 리포트 전문일 수도 있고, 다음 run 에
|
|
189
|
+
첨부되는 carry-in 본문(``clarification_response_with_sidecars``)일 수도
|
|
190
|
+
있다. §1 셀 추출의 단일 참조점 — 다른 모듈이 §1 레이아웃을 다시 훑지 않도록
|
|
191
|
+
이 함수로 통일한다.
|
|
192
|
+
"""
|
|
193
|
+
section = _section_1_slice(report_text)
|
|
194
|
+
if section is None:
|
|
195
|
+
return []
|
|
196
|
+
lines = section.splitlines()
|
|
197
|
+
header_idx = -1
|
|
198
|
+
for idx, line in enumerate(lines):
|
|
199
|
+
if not line.lstrip().startswith("|"):
|
|
200
|
+
continue
|
|
201
|
+
cells = [c.lower() for c in _split_pipe_row(line)]
|
|
202
|
+
if "user input" in cells and any(c.startswith("statement") for c in cells):
|
|
203
|
+
header_idx = idx
|
|
204
|
+
break
|
|
205
|
+
if header_idx < 0:
|
|
206
|
+
return []
|
|
207
|
+
header = [c.lower() for c in _split_pipe_row(lines[header_idx])]
|
|
208
|
+
s_col = next((j for j, h in enumerate(header) if h.startswith("statement")), -1)
|
|
209
|
+
e_col = next((j for j, h in enumerate(header) if h.startswith("expected form")), -1)
|
|
210
|
+
rows: list[dict] = []
|
|
211
|
+
body = False
|
|
212
|
+
for line in lines[header_idx + 1:]:
|
|
213
|
+
if not line.lstrip().startswith("|"):
|
|
214
|
+
if body:
|
|
215
|
+
break
|
|
216
|
+
continue
|
|
217
|
+
if is_separator_row(line):
|
|
218
|
+
body = True
|
|
219
|
+
continue
|
|
220
|
+
if not body:
|
|
221
|
+
continue
|
|
222
|
+
cells = _split_pipe_row(line)
|
|
223
|
+
item = parse_meta_cell(cells[0]) if cells else None
|
|
224
|
+
if item is None:
|
|
225
|
+
continue
|
|
226
|
+
rows.append({
|
|
227
|
+
"item": item,
|
|
228
|
+
"statement": cells[s_col] if 0 <= s_col < len(cells) else "",
|
|
229
|
+
"expected_form": cells[e_col] if 0 <= e_col < len(cells) else "",
|
|
230
|
+
# v1 cells are strings; the structured options live only in v2's
|
|
231
|
+
# data sibling. The key stays so callers never branch on schema.
|
|
232
|
+
"options": [],
|
|
233
|
+
})
|
|
234
|
+
return rows
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
_LOOSE_SECTION_1_RE = re.compile(r"^##\s+1\.\s+Clarification Items\b", re.MULTILINE)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def section_1_present_but_unparsed(report_text: str) -> bool:
|
|
241
|
+
"""§1 헤딩이 느슨 탐지엔 잡히지만 엄격 SECTION_HEADING_PATTERN 에는 매칭하지
|
|
242
|
+
못하는 경우 True — 헤딩 형태가 어긋나(앵커·포맷 drift) §1 슬라이스 자체가
|
|
243
|
+
실패하는 상태다.
|
|
244
|
+
|
|
245
|
+
이때 ``_section_1_slice`` 가 None 을 반환해 parse 가 통째로 None 이 되고 승인
|
|
246
|
+
게이트가 "schema 없음 → soft-pass" 로 조용히 열린다. §1 앵커 버그가 정확히 이
|
|
247
|
+
메커니즘으로 터졌다. 헤딩 자체가 없는 legacy 리포트(둘 다 불매칭)와, 엄격
|
|
248
|
+
매칭에 성공하는 정상 헤딩(테이블이 없는 emptyState placeholder 포함)은 False —
|
|
249
|
+
placeholder 는 헤딩이 멀쩡하므로 fail-closed 로 오인하지 않는다. 정규식만 넓혀 온
|
|
250
|
+
과거 수정과 달리, 이 판별은 "헤딩 형태 drift" 자체를 차단해 재발 클래스를 닫는다."""
|
|
251
|
+
if SECTION_HEADING_PATTERN.search(report_text):
|
|
252
|
+
return False
|
|
253
|
+
return bool(_LOOSE_SECTION_1_RE.search(report_text))
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""리포트에서 clarification 행을 읽는다 — 어느 스키마가 썼든.
|
|
2
|
+
|
|
3
|
+
schema-v2 는 `.data.json` 의 `clarificationItems[]`, schema-v1 은 §1 표다.
|
|
4
|
+
읽기 함수가 텍스트가 아니라 **경로**를 받는 이유가 이것이다.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from ..final_report_paths import final_report_data_path
|
|
9
|
+
from ..json_boundary import load_owned_object
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from .parsing import (
|
|
13
|
+
ClarificationItem,
|
|
14
|
+
parse_section_1_rows,
|
|
15
|
+
)
|
|
16
|
+
from .dispositions import (
|
|
17
|
+
clarification_disposition,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
STRUCTURED_REPORT_VERSIONS = frozenset({"2.0", "3.0"})
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _read_report_text(report_path: Path) -> str:
|
|
25
|
+
return report_path.read_text(encoding="utf-8", errors="replace")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _structured_report_data(report_path: Path) -> Optional[dict]:
|
|
29
|
+
"""``report_path`` 의 구조화 리포트 레코드, 아니면 ``None``.
|
|
30
|
+
|
|
31
|
+
파일이 없거나 JSON 이 깨졌거나 구조화 판본이 아니면 ``None`` — 호출자는 v1
|
|
32
|
+
마크다운 경로로 폴백하고, 진짜 구조화 리포트인데 레코드가 깨진 경우는 그
|
|
33
|
+
폴백이 "§1 없음" fail-closed 로 잡는다.
|
|
34
|
+
|
|
35
|
+
판본을 `2.0` 하나로 못박아 두던 동안 3.0 리포트가 여기서 ``None`` 이 됐고,
|
|
36
|
+
그러면 호출자가 JSON 본문에서 `## 1.` 헤딩을 찾다 실패해 **리포트 전문을
|
|
37
|
+
그대로 복사**했다 — 이 좁히기가 막으려던 바로 그 중복이다. 실측: 66K 짜리
|
|
38
|
+
추출본이 나와야 할 자리에 693K data.json 이 스테이징됐고, 패킷의
|
|
39
|
+
`## Clarification Carry-In Extract` 가 0행이 되어 워커가 사용자 답변을
|
|
40
|
+
전혀 받지 못했다.
|
|
41
|
+
"""
|
|
42
|
+
data_path = final_report_data_path(report_path)
|
|
43
|
+
if not data_path.is_file():
|
|
44
|
+
return None
|
|
45
|
+
try:
|
|
46
|
+
data = load_owned_object(data_path, artifact="final report record")
|
|
47
|
+
except (OSError, ValueError):
|
|
48
|
+
return None
|
|
49
|
+
if (
|
|
50
|
+
not isinstance(data, dict)
|
|
51
|
+
or data.get("schemaVersion") not in STRUCTURED_REPORT_VERSIONS
|
|
52
|
+
):
|
|
53
|
+
return None
|
|
54
|
+
return data
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _v2_row(entry: dict) -> Optional[dict]:
|
|
58
|
+
"""``clarificationItems[]`` 한 행을 §1 행과 같은 shape 으로. 필수 필드가
|
|
59
|
+
빠졌으면 ``None`` — 호출자가 fail-closed 로 셀 수 있게 한다."""
|
|
60
|
+
row_id = entry.get("id")
|
|
61
|
+
raw_blocks = entry.get("blocks")
|
|
62
|
+
raw_status = entry.get("status")
|
|
63
|
+
if not (isinstance(row_id, str) and row_id):
|
|
64
|
+
return None
|
|
65
|
+
if not (isinstance(raw_blocks, str) and raw_blocks):
|
|
66
|
+
return None
|
|
67
|
+
if not (isinstance(raw_status, str) and raw_status):
|
|
68
|
+
return None
|
|
69
|
+
kind = entry.get("kind")
|
|
70
|
+
item = ClarificationItem(
|
|
71
|
+
row_id=row_id,
|
|
72
|
+
kind=kind.lower() if isinstance(kind, str) else "",
|
|
73
|
+
blocks=raw_blocks.lower(),
|
|
74
|
+
status=raw_status.lower(),
|
|
75
|
+
raw_blocks=raw_blocks,
|
|
76
|
+
raw_status=raw_status,
|
|
77
|
+
)
|
|
78
|
+
options = entry.get("options")
|
|
79
|
+
return {
|
|
80
|
+
"item": item,
|
|
81
|
+
"statement": str(entry.get("statement") or ""),
|
|
82
|
+
"expected_form": str(entry.get("expectedForm") or ""),
|
|
83
|
+
"options": (
|
|
84
|
+
[option for option in options if isinstance(option, dict)]
|
|
85
|
+
if isinstance(options, list)
|
|
86
|
+
else []
|
|
87
|
+
),
|
|
88
|
+
"disposition": clarification_disposition(entry),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _v2_clarification_rows(report_path: Path) -> Optional[list[dict]]:
|
|
93
|
+
"""schema-v2 clarification 행들, 이 리포트가 v2 가 아니면 ``None``.
|
|
94
|
+
필수 필드가 빠진 행은 건너뛰는 lenient 계약(§1 파서와 동일)."""
|
|
95
|
+
data = _structured_report_data(report_path)
|
|
96
|
+
if data is None:
|
|
97
|
+
return None
|
|
98
|
+
entries = data.get("clarificationItems")
|
|
99
|
+
if not isinstance(entries, list):
|
|
100
|
+
return []
|
|
101
|
+
rows = [_v2_row(e) for e in entries if isinstance(e, dict)]
|
|
102
|
+
return [row for row in rows if row is not None]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def read_clarification_rows(report_path: Path) -> list[dict]:
|
|
106
|
+
"""리포트 한 건의 clarification 행 — schema-v2 는 data.json 사이드카에서,
|
|
107
|
+
schema-v1 은 §1 테이블에서. 스키마 버전을 아는 유일한 읽기 지점이다.
|
|
108
|
+
|
|
109
|
+
``parse_section_1_rows`` 와 같은 lenient 계약: 어느 쪽에서도 행을 못 찾으면
|
|
110
|
+
``[]``.
|
|
111
|
+
"""
|
|
112
|
+
v2_rows = _v2_clarification_rows(report_path)
|
|
113
|
+
if v2_rows is not None:
|
|
114
|
+
return v2_rows
|
|
115
|
+
return parse_section_1_rows(_read_report_text(report_path))
|