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,251 @@
|
|
|
1
|
+
"""차단(blocking) 검사 허용목록 — phase 를 멈출 수 있는 실패만 여기 등록한다.
|
|
2
|
+
|
|
3
|
+
배경: 검증 실패에는 등급이 없었다. 한 건이라도 나오면 run 이
|
|
4
|
+
`contract-violated` 가 되고 phase 가 전진하지 못한다
|
|
5
|
+
(`validators/validate-run.py` 의 `update_validation_metadata`,
|
|
6
|
+
`scripts/okstra_ctl/workflow.py` 의 `compute_workflow_state`). 그래서 리포트
|
|
7
|
+
문서의 상호참조 표기 하나가 실행 전체를 멈춰 세웠다.
|
|
8
|
+
|
|
9
|
+
실측 근거: 2026-08-20 이후 run-manifest 의 `validation.failures` 1209건 중
|
|
10
|
+
약 78% 가 리포트 문서 안의 기록·상호참조 항목이었다 — 승인 항목의 역추적
|
|
11
|
+
링크, `Evidence checked:` 문구, 활동 원장 backlink, 역할 이름 대조 같은
|
|
12
|
+
것들이다. 이 항목들이 틀려도 다음 단계가 읽는 파일은 그대로 읽히고,
|
|
13
|
+
렌더러도 그대로 돈다.
|
|
14
|
+
|
|
15
|
+
정책 — 허용목록(allowlist):
|
|
16
|
+
|
|
17
|
+
- 여기 등록된 조각(fragment)을 포함하는 실패만 run 을 실패시킨다.
|
|
18
|
+
- 등록되지 않은 실패는 advisory 로 강등된다. `validation.advisories` 에
|
|
19
|
+
그대로 남고 stderr 로도 출력되지만 phase 를 막지 않는다.
|
|
20
|
+
- 새 검사는 기본이 advisory 다. 차단이 필요하면 여기 한 줄을 추가하되,
|
|
21
|
+
**그 실패가 어떤 소비자를 실제로 깨뜨리는지** 함께 적는다. 그 문장을
|
|
22
|
+
쓸 수 없으면 차단이 아니라 advisory 다.
|
|
23
|
+
|
|
24
|
+
2026-08-28 정리(`.project-docs/reports/2026-08-28-validate-run-audit.md`):
|
|
25
|
+
도달 불가·중복 조각 5개를 빼고, 접두 일치로 문서 표기 검사를 끌어들이던 조각
|
|
26
|
+
2개를 조이고, 릴리스 경로를 여는 값 4계열과 self-mock 게이트를 승격했다.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
# (조각, 왜 차단인가 — 이 실패가 깨뜨리는 소비자)
|
|
32
|
+
#
|
|
33
|
+
# 조각은 실패 메시지에 substring 으로 존재하면 일치로 본다.
|
|
34
|
+
_BLOCKING: tuple[tuple[str, str], ...] = (
|
|
35
|
+
# 1. 다음 단계가 읽어야 할 산출물이 없거나 파싱되지 않는다.
|
|
36
|
+
(
|
|
37
|
+
"final-report data.json is missing",
|
|
38
|
+
"렌더러의 단일 입력이 없다 — 마크다운/HTML 산출 자체가 불가능하다.",
|
|
39
|
+
),
|
|
40
|
+
(
|
|
41
|
+
"final-report data.json is not valid JSON",
|
|
42
|
+
"같은 입력이 파싱되지 않는다.",
|
|
43
|
+
),
|
|
44
|
+
(
|
|
45
|
+
"final-report schema could not be loaded",
|
|
46
|
+
"스키마 없이는 데이터 형태를 판정할 수 없어 렌더 결과를 보증할 수 없다.",
|
|
47
|
+
),
|
|
48
|
+
(
|
|
49
|
+
"final-report data.json schema:",
|
|
50
|
+
"스키마 구조 위반 — 템플릿이 데이터를 순회하므로 필수 필드가 없으면 렌더가 깨진다.",
|
|
51
|
+
),
|
|
52
|
+
# 콜론이 붙어야 파일 부재 1건(validate-run.py 의
|
|
53
|
+
# `final report is missing: {report_path}`)만 잡는다. 콜론 없는 조각은
|
|
54
|
+
# `final report is missing the ...` 로 시작하는 문서 섹션 검사 3건(Token
|
|
55
|
+
# Usage Summary / Verdict Card / Index)까지 접두 일치로 끌어들여, 표시
|
|
56
|
+
# 제목을 차단 사유에서 뺀 ADR-0024 와 정면으로 어긋났다.
|
|
57
|
+
(
|
|
58
|
+
"final report is missing:",
|
|
59
|
+
"리포트 파일 자체가 없다.",
|
|
60
|
+
),
|
|
61
|
+
# 주어 없는 `is not importable` 은 어떤 모듈이 못 올라와도 차단이 됐다.
|
|
62
|
+
# 실제 메시지는 validate-run.py 의
|
|
63
|
+
# `validate-run: okstra_ctl.final_report_schema is not importable — ...`
|
|
64
|
+
# 한 곳뿐이다.
|
|
65
|
+
(
|
|
66
|
+
"okstra_ctl.final_report_schema is not importable",
|
|
67
|
+
"스키마 모듈이 없으면 final-report 를 검증하지 않고 통과시킨다 — "
|
|
68
|
+
"통과 판정 자체를 신뢰할 수 없다.",
|
|
69
|
+
),
|
|
70
|
+
(
|
|
71
|
+
"report-views: missing html artifact",
|
|
72
|
+
"열람본이 생성되지 않았다 — 사용자에게 전달할 산출물이 없다.",
|
|
73
|
+
),
|
|
74
|
+
(
|
|
75
|
+
"report-views validator timed out",
|
|
76
|
+
"열람본 생성 여부를 판정하지 못했다.",
|
|
77
|
+
),
|
|
78
|
+
# 타임아웃과 같은 등급이다. 둘 다 "열람본이 계약을 지켰는지 판정하지
|
|
79
|
+
# 못했다" 이고, 대리 스크립트 부재는 설치가 깨졌다는 뜻이라 이후 run 도
|
|
80
|
+
# 전부 같은 상태다.
|
|
81
|
+
(
|
|
82
|
+
"validate-report-views.py missing under",
|
|
83
|
+
"열람본 검증기가 설치본에 없다 — 판정 없이 통과가 된다.",
|
|
84
|
+
),
|
|
85
|
+
(
|
|
86
|
+
"conformance manifest unreadable",
|
|
87
|
+
"적합성 결과를 읽지 못해 게이트 통과 여부를 알 수 없다.",
|
|
88
|
+
),
|
|
89
|
+
(
|
|
90
|
+
"plan-body verification state file is unreadable",
|
|
91
|
+
"검증 라운드 상태를 읽지 못해 라운드 수행 여부를 알 수 없다.",
|
|
92
|
+
),
|
|
93
|
+
# 파싱 불가와 같은 등급이다. 소비자(`scripts/okstra_ctl/incremental_carry.py`
|
|
94
|
+
# 의 상태 파일 읽기)는 "파일이 없다" 와 "파일이 안 읽힌다" 를 구분하지
|
|
95
|
+
# 못하고 둘 다 라운드 이력 없음으로 떨어진다.
|
|
96
|
+
(
|
|
97
|
+
"`state/plan-body-verification-*.json` was written",
|
|
98
|
+
"덮어써진 검증 라운드의 유일한 기록이 없다 — 라운드 이력을 복원할 "
|
|
99
|
+
"방법이 사라진다.",
|
|
100
|
+
),
|
|
101
|
+
(
|
|
102
|
+
"is missing worker prompt history file",
|
|
103
|
+
"디스패치됐다고 기록된 워커가 받은 프롬프트가 없다 — 그 워커가 무엇을 "
|
|
104
|
+
"지시받았는지 확인할 방법이 없어 결과를 감사할 수 없다.",
|
|
105
|
+
),
|
|
106
|
+
(
|
|
107
|
+
"persisted initial prompt path is missing",
|
|
108
|
+
"같은 이유 — 워커의 최초 프롬프트 경로가 기록되지 않았다.",
|
|
109
|
+
),
|
|
110
|
+
# 2. 다음 단계가 읽는 입력이 실제 내용과 어긋난다.
|
|
111
|
+
#
|
|
112
|
+
# 여기에 수렴 분류 재생 불일치와 triggerEvidence 불일치는 없다. 둘 다
|
|
113
|
+
# 리포트 본문의 사실 주장이 틀렸다는 뜻이지만, 그 값을 읽어 실행을
|
|
114
|
+
# 구동하는 소비자가 없다 — 검증기와 리포트 투영(report_projections)만
|
|
115
|
+
# 읽는다. 프로그램 진행에는 문제가 없으므로 advisory 다.
|
|
116
|
+
(
|
|
117
|
+
"design-prep request content or fingerprint is stale",
|
|
118
|
+
"디자인 준비 요청이 실제 내용과 어긋난다 — implementation 단계가 "
|
|
119
|
+
"이 요청을 읽어 `wait_for_input` 까지 분기하므로"
|
|
120
|
+
"(scripts/okstra_ctl/implementation_stage.py:113) 옛 요청으로 진행한다.",
|
|
121
|
+
),
|
|
122
|
+
# selected-direction 계획의 두 조각. 주 집행은 조립 게시 게이트
|
|
123
|
+
# (report_assembly `_selected_direction_publication_errors`)이고, 여기는
|
|
124
|
+
# 게시 후 변조·우회 경로의 백스톱이다. dev-10341 실측: 이 접두 17건이
|
|
125
|
+
# advisory 로 통과 발행된 계획을 구현 진입이 그대로 하드 거부해 run 이
|
|
126
|
+
# wedge 됐다.
|
|
127
|
+
(
|
|
128
|
+
"implementation-planning selected-direction:",
|
|
129
|
+
"구현 진입(scripts/okstra_ctl/run.py `_validate_selected_implementation_plan`)"
|
|
130
|
+
"이 같은 검증을 하드 거부한다 — 통과 발행된 계획이 다음 phase 에서 "
|
|
131
|
+
"준비 자체가 안 된다.",
|
|
132
|
+
),
|
|
133
|
+
(
|
|
134
|
+
"has malformed conformanceTests declaration",
|
|
135
|
+
"승인 경계(scripts/okstra_ctl/run.py `_validate_approved_plan_conformance`)"
|
|
136
|
+
"가 같은 형식을 하드 거부한다 — 같은 이유로 구현 준비가 막힌다.",
|
|
137
|
+
),
|
|
138
|
+
# 3. 게이트 결과가 없거나 무의미하다.
|
|
139
|
+
#
|
|
140
|
+
# `conformance gate BLOCKING` 이 실제로 잡는 범위는 "스크립트가 안 돌았다"
|
|
141
|
+
# 한 갈래보다 넓다. validate-run.py 의 발생 지점 22곳은 세 갈래다 —
|
|
142
|
+
# 게이트 미실행, 승인된 계획 증거의 배관 실패(run-inputs 부재·approvedPlanPath
|
|
143
|
+
# 부재·경로 해석 실패), 선언 규칙 위반(선언되지 않은 경로를 만진 diff,
|
|
144
|
+
# 선언된 스크립트 누락). 세 갈래 전부 같은 소비자 문장으로 정당화된다:
|
|
145
|
+
# 이후 단계가 "이 stage 는 적합성 검사를 통과했다"를 전제로 진행하는데,
|
|
146
|
+
# 그 전제를 세울 근거 자체가 없다.
|
|
147
|
+
#
|
|
148
|
+
# 접두 결합에 주의한다. `missing_declared_scripts` 와
|
|
149
|
+
# `_declared_conformance_errors` 가 만든 임의 문자열이 이 접두 뒤에 붙어
|
|
150
|
+
# (`f"conformance gate BLOCKING: {error}"`) 여기 등록되지 않은 검사가
|
|
151
|
+
# 자동으로 차단 등급을 얻는다. 그 함수들에 새 오류 문구를 추가하는 것은
|
|
152
|
+
# 곧 이 허용목록에 한 줄을 추가하는 것과 같다.
|
|
153
|
+
(
|
|
154
|
+
"conformance gate BLOCKING",
|
|
155
|
+
"적합성 게이트의 결과가 없다 — 이후 단계가 적합성 전제를 세울 수 없다.",
|
|
156
|
+
),
|
|
157
|
+
# self-mock 게이트(PR #356)는 차단 목적으로 설계됐다. 테스트가 SUT 를
|
|
158
|
+
# 스텁하면 통과 판정이 "무엇도 검증하지 않았다"와 같은 뜻이 되고,
|
|
159
|
+
# final-verification 과 릴리스 게이트(scripts/okstra_ctl/release_gate.py)가
|
|
160
|
+
# 검증되지 않은 코드를 통과시킨다. validate-run.py 의 self-mock 실패는
|
|
161
|
+
# 전부 이 접두를 단다.
|
|
162
|
+
(
|
|
163
|
+
"self-mock gate BLOCKING:",
|
|
164
|
+
"테스트가 SUT 를 스텁해 통과 판정이 무의미하다 — 검증되지 않은 코드가 "
|
|
165
|
+
"릴리스 게이트를 지난다.",
|
|
166
|
+
),
|
|
167
|
+
# 4. 검증되지 않은 결과가 릴리스·후속 stage 로 넘어간다.
|
|
168
|
+
#
|
|
169
|
+
# 이 군은 전부 릴리스 경로를 여는 값을 읽는 소비자가 검증기 밖에 있다.
|
|
170
|
+
# 리포트 본문 표기가 아니라 판정 자체가 틀린 경우다.
|
|
171
|
+
(
|
|
172
|
+
"final-verification report declares",
|
|
173
|
+
"리포트가 선언한 verificationScope·worktree·base/head 가 준비된 검증 "
|
|
174
|
+
"대상과 다르다 — scripts/okstra_ctl/handoff.py 가 verificationScope 로 "
|
|
175
|
+
"stage-group 적격성과 release-handoff 라우팅을 정하므로, 검증하지 않은 "
|
|
176
|
+
"head 가 릴리스로 간다.",
|
|
177
|
+
),
|
|
178
|
+
(
|
|
179
|
+
"final-verification report covers stages",
|
|
180
|
+
"같은 이유 — 증거를 준비하지 않은 stage 집합에 판정이 내려진다.",
|
|
181
|
+
),
|
|
182
|
+
(
|
|
183
|
+
"`verdict: FAIL` but `finalVerdict.verdictToken` is",
|
|
184
|
+
"검증자가 기록한 FAIL 이 통과 판정과 공존한다 — release_gate.py 와 "
|
|
185
|
+
"stage_fix_carry.py 가 통과 판정만 보므로 거부된 작업이 "
|
|
186
|
+
"release-handoff 로 넘어간다.",
|
|
187
|
+
),
|
|
188
|
+
(
|
|
189
|
+
"implementation run declares stage-",
|
|
190
|
+
"stage 캐리 사이드카가 디스크에 없다 — consumers.py 의 "
|
|
191
|
+
"backfill_done_from_carry 가 이 파일로 stage 를 done 처리하므로, "
|
|
192
|
+
"없으면 후속 stage 가 전부 PrepareError 로 준비에 실패한다.",
|
|
193
|
+
),
|
|
194
|
+
# final-verification 판정의 교차 필드 모순. 세 조각으로 나눠 등록한 이유는
|
|
195
|
+
# `final-verification: ` 접두 하나로 묶으면 같은 접두를 쓰는
|
|
196
|
+
# addedSurfaceAudit 표기 검사 3건까지 차단으로 끌려오기 때문이다.
|
|
197
|
+
(
|
|
198
|
+
"final-verification: verdict ",
|
|
199
|
+
"판정과 blocker/condition 이 모순이다 — release_gate.py 와 "
|
|
200
|
+
"next_phase.py 가 이 판정으로 분기하므로 잘못된 다음 phase 가 열린다.",
|
|
201
|
+
),
|
|
202
|
+
(
|
|
203
|
+
"final-verification: routingRecommendation",
|
|
204
|
+
"같은 이유 — 라우팅 대상이 없거나 판정이 허용하지 않는 대상을 가리킨다.",
|
|
205
|
+
),
|
|
206
|
+
(
|
|
207
|
+
"final-verification: verificationScope ",
|
|
208
|
+
"같은 이유 — 검증 범위가 열거값 밖이거나 단일 stage 검증이 전체 "
|
|
209
|
+
"release-handoff 를 요구한다.",
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# 2026-08-28 에 제거한 조각과 근거 (감사 보고서 §2):
|
|
214
|
+
#
|
|
215
|
+
# - `failed to parse JSON:` — validate-run.py 의 `load_json` 이 이 문구로
|
|
216
|
+
# ValueError 를 raise 하는데, 호출부가 failures 리스트를 만들기 전이라
|
|
217
|
+
# 프로세스가 그대로 죽는다. partition() 에 도달하지 못한다.
|
|
218
|
+
# - `cannot load Stage Map validator` — `spec_from_file_location` 은 파일이
|
|
219
|
+
# 없어도 spec 을 돌려주므로 조건이 성립하지 않고, 파일 부재는 그 다음
|
|
220
|
+
# `exec_module` 에서 예외로 끝난다.
|
|
221
|
+
# - `missing or malformed at` — 유일 발생 지점(approvedPlanPath 부재)이
|
|
222
|
+
# `conformance gate BLOCKING:` 접두를 함께 달고 있어 고유 승격이 0 이었다.
|
|
223
|
+
# - `unreadable at` — 발생 지점 4곳 중 3곳이 `conformance gate BLOCKING:`
|
|
224
|
+
# 접두 2곳과 `conformance manifest unreadable at` 1곳으로 이미 덮이고,
|
|
225
|
+
# 남은 1곳(self-mock 사이드카)은 위 `self-mock gate BLOCKING:` 로 덮인다.
|
|
226
|
+
# 이 조각이 하던 일은 self-mock 게이트에서 "사이드카를 못 읽었다" 갈래만
|
|
227
|
+
# 차단하고 실제 FAIL·미검출 뮤턴트는 advisory 로 두는 등급 역전이었다.
|
|
228
|
+
# - `phase-boundary:` — 판정 근거가 리드가 손으로 쓴 에러 로그 산문의
|
|
229
|
+
# substring 스캔이었고 검사 자체가 삭제됐다.
|
|
230
|
+
|
|
231
|
+
BLOCKING_FRAGMENTS: tuple[str, ...] = tuple(fragment for fragment, _why in _BLOCKING)
|
|
232
|
+
|
|
233
|
+
BLOCKING_REASONS: dict[str, str] = {fragment: why for fragment, why in _BLOCKING}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def is_blocking(failure: str) -> bool:
|
|
237
|
+
"""``failure`` 가 허용목록에 걸리면 True."""
|
|
238
|
+
return any(fragment in failure for fragment in BLOCKING_FRAGMENTS)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def partition(failures: list[str]) -> tuple[list[str], list[str]]:
|
|
242
|
+
"""``failures`` 를 (차단, advisory) 로 나눈다.
|
|
243
|
+
|
|
244
|
+
순서는 입력 순서를 유지한다 — 리포트에 실릴 때 원래 검사 순서가 곧
|
|
245
|
+
읽는 순서이기 때문이다.
|
|
246
|
+
"""
|
|
247
|
+
blocking: list[str] = []
|
|
248
|
+
advisory: list[str] = []
|
|
249
|
+
for failure in failures:
|
|
250
|
+
(blocking if is_blocking(failure) else advisory).append(failure)
|
|
251
|
+
return blocking, advisory
|
|
@@ -45,12 +45,5 @@ def read_brief_frontmatter(path: Path) -> dict[str, str]:
|
|
|
45
45
|
return out
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def is_canonical_generated_brief(frontmatter: Mapping[str, str]) -> bool:
|
|
49
|
-
return (
|
|
50
|
-
frontmatter.get("type") == "brief"
|
|
51
|
-
and frontmatter.get("generator") == "okstra-brief-gen"
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
|
|
55
48
|
def has_reporter_confirmation_contract(frontmatter: Mapping[str, str]) -> bool:
|
|
56
49
|
return "reporter-confirmations" in frontmatter
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Read a final-report's clarification rows, whichever schema wrote them.
|
|
2
|
+
|
|
3
|
+
A clarification is what a run owes the user — a decision, a file attachment,
|
|
4
|
+
a single data point. Each row carries a ``Blocks`` value out of
|
|
5
|
+
``{approval, next-phase, none}``. Rows with ``Blocks=approval`` are the
|
|
6
|
+
approval gate: they MUST resolve before the user flips the frontmatter
|
|
7
|
+
``approved`` field to ``true`` and starts the next ``implementation`` run.
|
|
8
|
+
A ``request-revision`` / ``reject`` answer still blocks the report that has
|
|
9
|
+
not yet incorporated it. The report that recorded the same id in
|
|
10
|
+
``supersessionLedger`` has already absorbed that return and does not block.
|
|
11
|
+
|
|
12
|
+
The two schemas store those rows in different places, and that is why the
|
|
13
|
+
read functions take a report **path**, not its text:
|
|
14
|
+
|
|
15
|
+
* schema-v1 — the ``## 1. Clarification Items`` markdown table (introduced
|
|
16
|
+
when §4.5.9 / §5.1 / §5.2 collapsed into a single section).
|
|
17
|
+
* schema-v2 — ``clarificationItems[]`` in the ``.data.json`` sibling. Its AI
|
|
18
|
+
handoff markdown renders them as one section per row under
|
|
19
|
+
``## Clarification and User Decisions``, not as a §1 table, so the §1 table
|
|
20
|
+
walk finds nothing there by construction.
|
|
21
|
+
|
|
22
|
+
Every gate goes through ``scan_approval_gate`` / ``scan_open_user_input`` so
|
|
23
|
+
run-prep (``_validate_approved_plan``), the wizard, and the user-response CLI
|
|
24
|
+
cannot disagree about what is still open.
|
|
25
|
+
|
|
26
|
+
Gate semantics are fail-closed: when the rows cannot be read with confidence
|
|
27
|
+
(§1 heading missing/drifted, table header unrecognized, a body row whose
|
|
28
|
+
metadata cell fails to parse, or a v2 row missing id/blocks/status), the scan
|
|
29
|
+
reports an ``unreadable_reason`` and callers must refuse approval instead of
|
|
30
|
+
soft-passing. ``parse_clarification_items`` keeps the lenient
|
|
31
|
+
None-on-absence contract for the schema-v1 HTML-view renderers, which only
|
|
32
|
+
need best-effort row extraction.
|
|
33
|
+
|
|
34
|
+
구현은 여섯 층으로 나뉜다 — `parsing` · `dispositions` 가 바닥이고,
|
|
35
|
+
`rows` · `sidecars` 가 그 위, `scan` · `carry` 가 그 둘을 쓴다. 이 파일은
|
|
36
|
+
밖으로 나가는 이름만 모아 둔다.
|
|
37
|
+
"""
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
from .parsing import (
|
|
41
|
+
ClarificationItem,
|
|
42
|
+
_CELL_ANCHOR_RE,
|
|
43
|
+
_section_1_slice,
|
|
44
|
+
_split_pipe_row,
|
|
45
|
+
parse_clarification_items,
|
|
46
|
+
parse_meta_cell,
|
|
47
|
+
section_1_present_but_unparsed,
|
|
48
|
+
)
|
|
49
|
+
from .dispositions import (
|
|
50
|
+
APPROVAL_BLOCKS,
|
|
51
|
+
PROCEEDING_DISPOSITIONS,
|
|
52
|
+
USER_INPUT_BLOCKS,
|
|
53
|
+
clarification_disposition,
|
|
54
|
+
incorporated_clarification_ids,
|
|
55
|
+
progress_blocking_ids,
|
|
56
|
+
row_blocks_progress,
|
|
57
|
+
)
|
|
58
|
+
from .rows import (
|
|
59
|
+
STRUCTURED_REPORT_VERSIONS,
|
|
60
|
+
read_clarification_rows,
|
|
61
|
+
)
|
|
62
|
+
from .sidecars import (
|
|
63
|
+
attached_user_responses_section,
|
|
64
|
+
sidecar_answers,
|
|
65
|
+
user_response_sidecars,
|
|
66
|
+
)
|
|
67
|
+
from .scan import (
|
|
68
|
+
scan_approval_gate,
|
|
69
|
+
scan_open_user_input,
|
|
70
|
+
)
|
|
71
|
+
from .carry import (
|
|
72
|
+
UNRESOLVED_STATUSES,
|
|
73
|
+
carried_clarification_rows,
|
|
74
|
+
clarification_response_with_sidecars,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
__all__ = [
|
|
78
|
+
"APPROVAL_BLOCKS",
|
|
79
|
+
"ClarificationItem",
|
|
80
|
+
"PROCEEDING_DISPOSITIONS",
|
|
81
|
+
"STRUCTURED_REPORT_VERSIONS",
|
|
82
|
+
"UNRESOLVED_STATUSES",
|
|
83
|
+
"USER_INPUT_BLOCKS",
|
|
84
|
+
"_CELL_ANCHOR_RE",
|
|
85
|
+
"_section_1_slice",
|
|
86
|
+
"_split_pipe_row",
|
|
87
|
+
"attached_user_responses_section",
|
|
88
|
+
"carried_clarification_rows",
|
|
89
|
+
"clarification_disposition",
|
|
90
|
+
"clarification_response_with_sidecars",
|
|
91
|
+
"incorporated_clarification_ids",
|
|
92
|
+
"parse_clarification_items",
|
|
93
|
+
"parse_meta_cell",
|
|
94
|
+
"progress_blocking_ids",
|
|
95
|
+
"read_clarification_rows",
|
|
96
|
+
"row_blocks_progress",
|
|
97
|
+
"scan_approval_gate",
|
|
98
|
+
"scan_open_user_input",
|
|
99
|
+
"section_1_present_but_unparsed",
|
|
100
|
+
"sidecar_answers",
|
|
101
|
+
"user_response_sidecars",
|
|
102
|
+
]
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""다음 run 으로 넘길 clarification 응답 본문을 만든다.
|
|
2
|
+
|
|
3
|
+
이전 리포트의 §1 과 사용자 답변 사이드카를 합쳐 하나의 이어받기 본문으로
|
|
4
|
+
정리한다. 답이 온 행은 상태를 갱신하고, 아직 열린 행은 그대로 넘긴다.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from ..md_table import is_separator_row, split_pipe_row, to_cell_text
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
import re
|
|
12
|
+
from .parsing import (
|
|
13
|
+
SECTION_HEADING_PATTERN,
|
|
14
|
+
_section_1_slice,
|
|
15
|
+
_split_pipe_row,
|
|
16
|
+
parse_meta_cell,
|
|
17
|
+
)
|
|
18
|
+
from .rows import (
|
|
19
|
+
_structured_report_data,
|
|
20
|
+
)
|
|
21
|
+
from .sidecars import (
|
|
22
|
+
attached_user_responses_section,
|
|
23
|
+
sidecar_answers,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
UNRESOLVED_STATUSES = {"open"}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def clarification_response_with_sidecars(source: Path) -> str:
|
|
31
|
+
"""clarification-response 본문에 `user-responses/` 사이드카를 덧붙인 본문.
|
|
32
|
+
|
|
33
|
+
`resume-clarification` 의 설계된 입력은 사용자가 §1 의 `User input` 열을
|
|
34
|
+
채운 **직전 final-report 자체**다. 그 전문을 그대로 복사하면 리포트가
|
|
35
|
+
instruction-set 안에 두 번째로 존재하게 되고, report-writer 는 같은 내용을
|
|
36
|
+
`clarification-response.md` 로 한 번, 직전 리포트 경로로 다시 한 번 읽는다.
|
|
37
|
+
리포트는 run 이 누적될수록 커지므로 이 중복은 스스로 악화된다(실측: 283K
|
|
38
|
+
리포트가 892K 의 중복 읽기를 만들어 report-writer 를 timeout 시켰다).
|
|
39
|
+
|
|
40
|
+
그래서 소스가 final-report 일 때는 답변이 실린 clarification 행만 잘라내고
|
|
41
|
+
원문은 경로로 가리킨다 — `attached_user_responses_section` 이 implementation
|
|
42
|
+
carry-in 에서 이미 쓰는 "원문은 경로로, 답변만 첨부" 규칙과 같다. 행을 어디서
|
|
43
|
+
읽는지는 스키마가 정한다(v1 은 §1 표, v2 는 data.json). clarification 을
|
|
44
|
+
아예 담지 않은 소스(사용자가 직접 쓴 답변 파일)만 원문 그대로 복사한다.
|
|
45
|
+
"""
|
|
46
|
+
text = source.read_text(encoding="utf-8")
|
|
47
|
+
section = attached_user_responses_section(source)
|
|
48
|
+
answers = sidecar_answers(source)
|
|
49
|
+
body = _clarification_carry_body(source, text, answers)
|
|
50
|
+
if not section:
|
|
51
|
+
return body
|
|
52
|
+
return body.rstrip("\n") + "\n\n---\n\n" + section
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
SECTION_1_HEADING = "## 1. Clarification Items"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_SECTION_1_TABLE_HEADER = (
|
|
59
|
+
"| Record | Statement | Expected form | User input |\n"
|
|
60
|
+
"|---|---|---|---|"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
_SECTION_1_EMPTY_STATE = "- The source report recorded no clarification items."
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _clarification_carry_body(
|
|
68
|
+
source: Path, text: str, answers: dict[str, str]
|
|
69
|
+
) -> str:
|
|
70
|
+
"""final-report 소스는 §1 + 원문 포인터로 좁히고, 그 외는 원문 그대로.
|
|
71
|
+
|
|
72
|
+
§1 이 있으면 사이드카 답변을 그 표의 `User input` 열에 병합해, 답이 표 안에
|
|
73
|
+
자리하도록 한다(파일 헤더가 선언하는 "답은 User input 열에" 계약을 실제로
|
|
74
|
+
참으로 만든다)."""
|
|
75
|
+
carried = _carry_section_1(source, text)
|
|
76
|
+
if carried is None:
|
|
77
|
+
return text
|
|
78
|
+
heading, section_body = carried
|
|
79
|
+
if answers:
|
|
80
|
+
section_body = _reconcile_user_input(section_body, answers)
|
|
81
|
+
return (
|
|
82
|
+
"# Clarification Response (carry-in)\n\n"
|
|
83
|
+
f"- Source report: `{source}`\n"
|
|
84
|
+
"- This file carries **only** the source report's Clarification Items "
|
|
85
|
+
"section; the report itself is read from the path above when a phase "
|
|
86
|
+
"needs it. Do not re-read the source report to find the answers — they "
|
|
87
|
+
"are in the `User input` column below.\n\n"
|
|
88
|
+
f"{heading}\n{section_body}\n"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def carried_clarification_rows(source: Path) -> str:
|
|
93
|
+
"""``source`` 리포트의 §1 표 본문만. clarification 을 담지 않았으면 빈 문자열.
|
|
94
|
+
|
|
95
|
+
`clarification_response_with_sidecars` 가 carry-in 본문에 싣는 것과 **같은**
|
|
96
|
+
잘라내기다. 직전 계획 run 의 clarification 을 packet 에 실을 때 이 함수를
|
|
97
|
+
쓰지 않고 따로 긁으면 같은 리포트가 소비처마다 다른 clarification 집합으로
|
|
98
|
+
읽힌다. 리포트 전문은 절대 싣지 않는다 — 그것이 이 모듈이 존재하는 이유다.
|
|
99
|
+
"""
|
|
100
|
+
carried = _carry_section_1(source)
|
|
101
|
+
return "" if carried is None else carried[1].strip()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _carry_section_1(
|
|
105
|
+
source: Path, text: Optional[str] = None
|
|
106
|
+
) -> Optional[tuple[str, str]]:
|
|
107
|
+
"""carry-in 본문에 실을 (헤딩, §1 본문). clarification 을 담지 않은 소스면
|
|
108
|
+
``None``.
|
|
109
|
+
|
|
110
|
+
schema-v2 는 행을 data.json 에 들고 AI 마크다운에는 §1 표가 없다. §1 슬라이스
|
|
111
|
+
만 보던 동안 v2 소스는 "좁힐 것이 없다" 로 판정돼 **리포트 전문이 그대로
|
|
112
|
+
복사**됐다 — 이 좁히기가 막으려던 바로 그 중복이다. carry-in 은 파생 문서이고
|
|
113
|
+
다운스트림(승인 게이트·프롬프트 빌더·검증 워커)이 §1 표 하나만 읽으므로, v2
|
|
114
|
+
행도 같은 표로 렌더한다.
|
|
115
|
+
|
|
116
|
+
``text`` 를 생략하면 v1 슬라이스가 필요할 때만 소스를 읽는다. v2/v3 소스는
|
|
117
|
+
본문이 data.json 에 있어 마크다운을 읽을 이유가 없고, 그 파일은 실측 693K
|
|
118
|
+
까지 커진다."""
|
|
119
|
+
data = _structured_report_data(source)
|
|
120
|
+
if data is not None:
|
|
121
|
+
entries = data.get("clarificationItems")
|
|
122
|
+
return SECTION_1_HEADING, _structured_section_1_body(
|
|
123
|
+
entries if isinstance(entries, list) else []
|
|
124
|
+
)
|
|
125
|
+
if text is None:
|
|
126
|
+
text = source.read_text(encoding="utf-8", errors="replace")
|
|
127
|
+
slice_ = _section_1_slice(text)
|
|
128
|
+
if slice_ is None:
|
|
129
|
+
return None
|
|
130
|
+
heading = SECTION_HEADING_PATTERN.search(text)
|
|
131
|
+
assert heading is not None # _section_1_slice returned a slice
|
|
132
|
+
return heading.group(0), slice_.rstrip()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _structured_section_1_body(entries: list) -> str:
|
|
136
|
+
"""schema-v2 `clarificationItems[]` 를 §1 표 본문으로.
|
|
137
|
+
|
|
138
|
+
메타 셀은 렌더러가 쓰는 모양 그대로다 — `Status:` 는 따옴표 없이 써야
|
|
139
|
+
`_reconcile_user_input` 이 답을 병합하면서 상태를 answered 로 넘길 수 있다."""
|
|
140
|
+
rows = [e for e in entries if isinstance(e, dict) and e.get("id")]
|
|
141
|
+
if not rows:
|
|
142
|
+
return f"\n{_SECTION_1_EMPTY_STATE}"
|
|
143
|
+
lines = ["", _SECTION_1_TABLE_HEADER]
|
|
144
|
+
for entry in rows:
|
|
145
|
+
meta = (
|
|
146
|
+
f"**{entry['id']}**"
|
|
147
|
+
f"<br>Ticket: `{to_cell_text(entry.get('ticketId'))}`"
|
|
148
|
+
f"<br>Kind: `{to_cell_text(entry.get('kind'))}`"
|
|
149
|
+
f"<br>Blocks: `{to_cell_text(entry.get('blocks'))}`"
|
|
150
|
+
f"<br>Status: {to_cell_text(entry.get('status'))}"
|
|
151
|
+
)
|
|
152
|
+
lines.append(
|
|
153
|
+
f"| {meta} | {to_cell_text(entry.get('statement'))} "
|
|
154
|
+
f"| {to_cell_text(entry.get('expectedForm'))} "
|
|
155
|
+
f"| {to_cell_text(entry.get('userInput'))} |"
|
|
156
|
+
)
|
|
157
|
+
return "\n".join(lines)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
_STATUS_ANSWER_RE = re.compile(r"(Status:\s*)(?:open|answered)\b", re.IGNORECASE)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _locate_user_input_column(lines: list[str]) -> tuple[int, int]:
|
|
164
|
+
"""§1 데이터 표의 헤더 줄 인덱스와 `User input` 열 인덱스. 표가 없으면 (-1, -1)."""
|
|
165
|
+
for idx, line in enumerate(lines):
|
|
166
|
+
if not line.lstrip().startswith("|"):
|
|
167
|
+
continue
|
|
168
|
+
cells = [c.lower() for c in _split_pipe_row(line)]
|
|
169
|
+
if "user input" in cells and any(c.startswith("statement") for c in cells):
|
|
170
|
+
return idx, cells.index("user input")
|
|
171
|
+
return -1, -1
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _reconcile_row(line: str, ui_col: int, answers: dict[str, str]) -> str:
|
|
175
|
+
"""답이 있고 open/answered 인 행이면 `User input` 칸을 그 답으로 채우고 Status 를
|
|
176
|
+
answered 로 바꾼 줄을, 그 외에는 원본 줄을 그대로 돌려준다.
|
|
177
|
+
|
|
178
|
+
칸에 이미 값이 있어도 사용자의 사이드카 답이 이긴다. 그 칸을 채우는 것은
|
|
179
|
+
run 자신(직전 렌더가 옮겨 적은 값)이고, 사용자가 나중에 답을 바꾸면 둘이
|
|
180
|
+
갈라진다 — 사용자가 쓴 쪽을 정본으로 삼지 않으면 run 이 자기가 적어둔 값으로
|
|
181
|
+
계속 되돌아간다.
|
|
182
|
+
|
|
183
|
+
판정은 앵커/백틱을 벗긴 셀(`_split_pipe_row`)로 — 그래야 `_meta_id` 가 스크롤
|
|
184
|
+
앵커의 소문자 slug 대신 진짜 대문자 ID 를 읽는다. 재조립은 원본 셀
|
|
185
|
+
(`split_pipe_row`)로 해서 앵커를 보존한다."""
|
|
186
|
+
norm = _split_pipe_row(line)
|
|
187
|
+
item = parse_meta_cell(norm[0]) if norm else None
|
|
188
|
+
if item is None or item.row_id not in answers:
|
|
189
|
+
return line
|
|
190
|
+
if item.status not in UNRESOLVED_STATUSES:
|
|
191
|
+
return line
|
|
192
|
+
raw = split_pipe_row(line)
|
|
193
|
+
if not 0 <= ui_col < len(raw):
|
|
194
|
+
return line
|
|
195
|
+
raw[ui_col] = answers[item.row_id]
|
|
196
|
+
raw[0] = _STATUS_ANSWER_RE.sub(r"\1answered", raw[0])
|
|
197
|
+
return "| " + " | ".join(to_cell_text(c) for c in raw) + " |"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _reconcile_user_input(section: str, answers: dict[str, str]) -> str:
|
|
201
|
+
"""§1 표에서 사이드카 답이 있는 미해결 행의 `User input` 칸을 답으로 채우고
|
|
202
|
+
Status 를 answered 로 바꾼 §1 본문을 돌려준다.
|
|
203
|
+
|
|
204
|
+
답의 정본 위치를 §1 표 안으로 옮긴다 — 표만 읽는 승인 게이트·프롬프트
|
|
205
|
+
빌더·검증 워커가 모두 답을 보게 하려는 것. 사이드카는 §1 표 밖 별도 섹션에만
|
|
206
|
+
있어서 표만 신뢰하는 소비자는 그 답을 놓쳤다."""
|
|
207
|
+
lines = section.splitlines()
|
|
208
|
+
header_idx, ui_col = _locate_user_input_column(lines)
|
|
209
|
+
if header_idx < 0:
|
|
210
|
+
return section
|
|
211
|
+
out = list(lines)
|
|
212
|
+
body = False
|
|
213
|
+
for i in range(header_idx + 1, len(lines)):
|
|
214
|
+
line = lines[i]
|
|
215
|
+
if not line.lstrip().startswith("|"):
|
|
216
|
+
if body:
|
|
217
|
+
break
|
|
218
|
+
continue
|
|
219
|
+
if is_separator_row(line):
|
|
220
|
+
body = True
|
|
221
|
+
continue
|
|
222
|
+
if body:
|
|
223
|
+
out[i] = _reconcile_row(line, ui_col, answers)
|
|
224
|
+
return "\n".join(out)
|