okstra 0.186.7 → 0.188.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli-registry.d.mts +88 -1
- package/dist/cli-registry.mjs +68 -111
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +0 -1
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +8 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.mjs +1 -14
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/config.mjs +38 -40
- package/dist/commands/lifecycle/config.mjs.map +1 -1
- package/dist/commands/lifecycle/doctor.d.mts +22 -7
- package/dist/commands/lifecycle/doctor.mjs +77 -49
- package/dist/commands/lifecycle/doctor.mjs.map +1 -1
- package/dist/commands/lifecycle/install.d.mts +12 -10
- package/dist/commands/lifecycle/install.mjs +107 -40
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/lifecycle/paths.mjs +8 -3
- package/dist/commands/lifecycle/paths.mjs.map +1 -1
- package/dist/commands/lifecycle/preflight.mjs +2 -1
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/lifecycle/setup.mjs +22 -36
- package/dist/commands/lifecycle/setup.mjs.map +1 -1
- package/dist/commands/lifecycle/uninstall.d.mts +4 -2
- package/dist/commands/lifecycle/uninstall.mjs +59 -15
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
- package/dist/commands/memory/memory.mjs +7 -1
- package/dist/commands/memory/memory.mjs.map +1 -1
- package/dist/lib/helper-scripts.d.mts +1 -1
- package/dist/lib/helper-scripts.mjs +10 -18
- package/dist/lib/helper-scripts.mjs.map +1 -1
- package/dist/lib/host-config.d.mts +72 -0
- package/dist/lib/host-config.mjs +404 -0
- package/dist/lib/host-config.mjs.map +1 -0
- package/dist/lib/host-registry-client.d.mts +2 -2
- package/dist/lib/host-registry-client.mjs +0 -3
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/dist/lib/install-assets.d.mts +1 -0
- package/dist/lib/install-assets.mjs +4 -0
- package/dist/lib/install-assets.mjs.map +1 -1
- package/dist/lib/proc.d.mts +2 -0
- package/dist/lib/proc.mjs +12 -0
- package/dist/lib/proc.mjs.map +1 -1
- package/dist/lib/python-command.d.mts +2 -0
- package/dist/lib/python-command.mjs +52 -0
- package/dist/lib/python-command.mjs.map +1 -0
- package/dist/lib/python-helper.d.mts +2 -5
- package/dist/lib/python-helper.mjs +3 -52
- package/dist/lib/python-helper.mjs.map +1 -1
- package/dist/lib/runtime-payload.d.mts +23 -0
- package/dist/lib/runtime-payload.mjs +57 -0
- package/dist/lib/runtime-payload.mjs.map +1 -0
- package/dist/lib/types.d.mts +24 -13
- package/docs/architecture/storage-model.md +21 -5
- package/docs/architecture.md +39 -38
- package/docs/cli.md +55 -76
- package/docs/coding-rules.md +295 -0
- package/docs/container.md +10 -36
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/skills/okstra-code-review.md +0 -1
- package/docs/for-ai/skills/okstra-container-build.md +8 -41
- package/docs/for-ai/skills/okstra-inspect.md +4 -4
- package/docs/for-ai/skills/okstra-manager.md +2 -2
- package/docs/for-ai/skills/okstra-rollup.md +0 -1
- package/docs/for-ai/skills/okstra-run.md +3 -3
- package/docs/for-ai/skills/okstra-user-response.md +0 -1
- package/docs/performance-improvement-plan-v2.md +1 -1
- package/docs/project-structure-overview.md +62 -62
- package/docs/task-process/README.md +3 -3
- package/docs/task-process/common-flow.md +1 -1
- package/docs/task-process/error-analysis.md +3 -3
- package/docs/task-process/implementation-planning.md +3 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +6 -11
- package/runtime/agents/workers/report-writer-worker.md +1 -1
- package/runtime/bin/lib/okstra/cli.sh +4 -0
- package/runtime/bin/lib/okstra/globals.sh +2 -0
- package/runtime/bin/lib/okstra/interactive.sh +26 -173
- package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
- package/runtime/bin/lib/okstra/usage.sh +3 -3
- package/runtime/bin/okstra-compact-reminder.sh +1 -5
- package/runtime/bin/okstra-error-log.py +19 -2
- package/runtime/bin/okstra-import-check.py +26 -0
- package/runtime/bin/okstra-inject-report-index.py +5 -4
- package/runtime/bin/okstra-provider-exec.py +22 -17
- package/runtime/bin/okstra-render-final-report.py +16 -6
- package/runtime/bin/okstra-render-report-views.py +47 -22
- package/runtime/bin/okstra-report-translate.py +30 -18
- package/runtime/bin/okstra-spawn-followups.py +4 -0
- package/runtime/bin/okstra-token-usage.py +3 -0
- package/runtime/bin/okstra.sh +4 -2
- package/runtime/bin/okstra_bootstrap.py +58 -0
- package/runtime/prompts/coding-preflight/overview.md +1 -1
- package/runtime/prompts/duties/planning-worker.md +1 -1
- package/runtime/prompts/launch.template.md +35 -9
- package/runtime/prompts/lead/adapters/cmux.md +3 -3
- package/runtime/prompts/lead/convergence.md +99 -88
- package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
- package/runtime/prompts/lead/plan-body-verification.md +65 -46
- package/runtime/prompts/lead/report-writer.md +35 -5
- package/runtime/prompts/lead/team-contract.md +4 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +3 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
- package/runtime/prompts/profiles/error-analysis.md +4 -3
- package/runtime/prompts/profiles/final-verification.md +2 -2
- package/runtime/prompts/profiles/implementation-planning.md +22 -17
- package/runtime/prompts/profiles/implementation.md +3 -2
- package/runtime/prompts/profiles/requirements-discovery.md +2 -1
- package/runtime/prompts/wizard/prompts.ko.json +29 -8
- package/runtime/python/okstra_ctl/__init__.py +6 -27
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +63 -22
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +20 -21
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
- package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
- package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
- package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +16 -2
- package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
- package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +228 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +605 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
- package/runtime/python/okstra_ctl/approval_decisions.py +287 -10
- package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
- package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
- package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
- package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
- package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
- package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
- package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
- package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
- package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
- package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
- package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
- package/runtime/python/okstra_ctl/cmux.py +38 -31
- package/runtime/python/okstra_ctl/code_review_target.py +163 -1
- package/runtime/python/okstra_ctl/conformance.py +23 -0
- package/runtime/python/okstra_ctl/container.py +38 -421
- package/runtime/python/okstra_ctl/context_cost.py +32 -4
- package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
- package/runtime/python/okstra_ctl/convergence.py +107 -16
- package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
- package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
- package/runtime/python/okstra_ctl/convergence_store.py +5 -29
- package/runtime/python/okstra_ctl/design_prep.py +108 -12
- package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
- package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
- package/runtime/python/okstra_ctl/dispatch_core.py +236 -49
- package/runtime/python/okstra_ctl/dispatch_state.py +417 -41
- package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +28 -1
- package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
- package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
- package/runtime/python/okstra_ctl/error_log_core.py +1 -1
- package/runtime/python/okstra_ctl/error_report.py +14 -0
- package/runtime/python/okstra_ctl/error_zip.py +18 -2
- package/runtime/python/okstra_ctl/exact_coverage.py +5 -0
- package/runtime/python/okstra_ctl/execution_identity.py +100 -7
- package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
- package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +319 -17
- package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
- package/runtime/python/okstra_ctl/handoff.py +24 -1
- package/runtime/python/okstra_ctl/ids.py +6 -16
- package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
- package/runtime/python/okstra_ctl/implementation_options.py +32 -13
- package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
- package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
- package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
- package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
- package/runtime/python/okstra_ctl/index.py +2 -2
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
- package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
- package/runtime/python/okstra_ctl/json_boundary.py +10 -0
- package/runtime/python/okstra_ctl/listing.py +0 -91
- package/runtime/python/okstra_ctl/locks.py +5 -19
- package/runtime/python/okstra_ctl/log_report.py +14 -0
- package/runtime/python/okstra_ctl/manager_cli.py +22 -3
- package/runtime/python/okstra_ctl/manager_launch.py +4 -8
- package/runtime/python/okstra_ctl/material.py +2 -2
- package/runtime/python/okstra_ctl/migrate.py +23 -1
- package/runtime/python/okstra_ctl/model_cli.py +19 -5
- package/runtime/python/okstra_ctl/model_discovery.py +46 -47
- package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
- package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
- package/runtime/python/okstra_ctl/model_io/references.py +370 -0
- package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
- package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
- package/runtime/python/okstra_ctl/model_pool.py +10 -6
- package/runtime/python/okstra_ctl/models.py +8 -8
- package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
- package/runtime/python/okstra_ctl/next_phase.py +121 -69
- package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
- package/runtime/python/okstra_ctl/pane_title.py +11 -8
- package/runtime/python/okstra_ctl/path_hints.py +36 -11
- package/runtime/python/okstra_ctl/paths.py +76 -8
- package/runtime/python/okstra_ctl/plan_items.py +132 -16
- package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
- package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
- package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
- package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
- package/runtime/python/okstra_ctl/prepare_error.py +20 -0
- package/runtime/python/okstra_ctl/prior_planning.py +157 -0
- package/runtime/python/okstra_ctl/profile_show.py +18 -1
- package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
- package/runtime/python/okstra_ctl/recap.py +22 -1
- package/runtime/python/okstra_ctl/reconcile.py +84 -0
- package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
- package/runtime/python/okstra_ctl/render.py +98 -32
- package/runtime/python/okstra_ctl/render_final_report.py +4 -8
- package/runtime/python/okstra_ctl/report_assembly.py +297 -7
- package/runtime/python/okstra_ctl/report_contract.py +30 -7
- package/runtime/python/okstra_ctl/report_finalize.py +213 -16
- package/runtime/python/okstra_ctl/report_html/common.py +2 -2
- package/runtime/python/okstra_ctl/report_html/render.py +8 -12
- package/runtime/python/okstra_ctl/report_language.py +8 -4
- package/runtime/python/okstra_ctl/report_narrative.py +220 -34
- package/runtime/python/okstra_ctl/report_projections.py +8 -2
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +393 -1
- package/runtime/python/okstra_ctl/report_translation.py +2 -35
- package/runtime/python/okstra_ctl/report_view_artifacts.py +42 -2
- package/runtime/python/okstra_ctl/report_views.py +4 -22
- package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
- package/runtime/python/okstra_ctl/rollup.py +13 -0
- package/runtime/python/okstra_ctl/run.py +639 -138
- package/runtime/python/okstra_ctl/run_audit.py +13 -0
- package/runtime/python/okstra_ctl/run_index_row.py +0 -12
- package/runtime/python/okstra_ctl/seeding.py +21 -11
- package/runtime/python/okstra_ctl/sequence.py +1 -1
- package/runtime/python/okstra_ctl/session.py +64 -14
- package/runtime/python/okstra_ctl/set_work_status.py +18 -0
- package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
- package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
- package/runtime/python/okstra_ctl/stage_map.py +57 -11
- package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
- package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
- package/runtime/python/okstra_ctl/stage_targets.py +3 -12
- package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
- package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
- package/runtime/python/okstra_ctl/task_target.py +4 -16
- package/runtime/python/okstra_ctl/team.py +49 -2
- package/runtime/python/okstra_ctl/time_report.py +17 -2
- package/runtime/python/okstra_ctl/usage_report.py +11 -0
- package/runtime/python/okstra_ctl/user_response.py +55 -218
- package/runtime/python/okstra_ctl/user_response_values.py +242 -0
- package/runtime/python/okstra_ctl/validation_contract.py +3 -0
- package/runtime/python/okstra_ctl/verdict_blocks.py +28 -8
- package/runtime/python/okstra_ctl/verification_target.py +68 -0
- package/runtime/python/okstra_ctl/wizard.py +457 -92
- package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
- package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
- package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
- package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
- package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
- package/runtime/python/okstra_ctl/worker_request.py +2 -1
- package/runtime/python/okstra_ctl/worker_runner.py +58 -17
- package/runtime/python/okstra_ctl/worker_state.py +16 -1
- package/runtime/python/okstra_ctl/workflow.py +18 -1
- package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
- package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
- package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
- package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
- package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
- package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
- package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
- package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
- package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
- package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
- package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
- package/runtime/python/okstra_ctl/write_policy.py +90 -213
- package/runtime/python/okstra_project/__init__.py +0 -4
- package/runtime/python/okstra_project/dirs.py +2 -2
- package/runtime/python/okstra_project/phase_pointer.py +84 -0
- package/runtime/python/okstra_project/slug.py +24 -0
- package/runtime/python/okstra_project/state.py +22 -230
- package/runtime/python/okstra_token_usage/antigravity.py +50 -12
- package/runtime/python/okstra_token_usage/claude.py +9 -1
- package/runtime/python/okstra_token_usage/cli.py +3 -1
- package/runtime/python/okstra_token_usage/collect.py +171 -36
- package/runtime/python/okstra_token_usage/cursor.py +2 -0
- package/runtime/python/okstra_token_usage/grok.py +1 -5
- package/runtime/python/okstra_token_usage/paths.py +26 -0
- package/runtime/python/okstra_token_usage/pricing.py +23 -8
- package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
- package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
- package/runtime/schemas/final-report-v2.0.schema.json +9 -26
- package/runtime/schemas/final-report-v3.0.schema.json +173 -41
- package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
- package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
- package/runtime/skills/okstra-container-build/SKILL.md +16 -47
- package/runtime/skills/okstra-inspect/facets/history.md +1 -1
- package/runtime/skills/okstra-inspect/facets/status.md +7 -6
- package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +7 -5
- package/runtime/templates/report-writer-prompt-preamble.md +3 -1
- package/runtime/templates/reports/brief.template.md +6 -2
- package/runtime/templates/reports/error-analysis-input.template.md +2 -0
- package/runtime/templates/reports/final-verification-input.template.md +2 -0
- package/runtime/templates/reports/implementation-input.template.md +7 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
- package/runtime/templates/reports/quick-input.template.md +2 -0
- package/runtime/templates/reports/release-handoff-input.template.md +6 -1
- package/runtime/templates/reports/report.js +20 -3
- package/runtime/templates/reports/schedule.template.md +2 -0
- package/runtime/templates/reports/settings.template.json +0 -10
- package/runtime/templates/reports/task-brief.template.md +3 -0
- package/runtime/templates/reports/user-response.template.md +7 -3
- package/runtime/validators/checks/fixtures-01.py +57 -0
- package/runtime/validators/checks/fixtures-02.py +565 -0
- package/runtime/validators/checks/runners-01.py +108 -0
- package/runtime/validators/checks/validate-assets-01.py +60 -0
- package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
- package/runtime/validators/checks/validate-tasks-01.py +46 -0
- package/runtime/validators/checks/validate-tasks-02.py +85 -0
- package/runtime/validators/checks/validate-tasks-03.py +61 -0
- package/runtime/validators/checks/validate-tasks-04.py +118 -0
- package/runtime/validators/forbidden_actions.py +73 -3
- package/runtime/validators/lib/common.sh +5 -0
- package/runtime/validators/lib/fixtures.sh +7 -591
- package/runtime/validators/lib/paths.sh +13 -0
- package/runtime/validators/lib/runners.sh +6 -104
- package/runtime/validators/lib/summary.sh +1 -1
- package/runtime/validators/lib/validate-assets.sh +6 -56
- package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
- package/runtime/validators/lib/validate-tasks.sh +9 -294
- package/runtime/validators/validate-implementation-plan-stages.py +4 -4
- package/runtime/validators/validate-run.py +1363 -2643
- package/runtime/validators/validate-workflow.sh +56 -16
- package/runtime/validators/validate_improvement_report.py +2 -1
- package/runtime/validators/validate_session_conformance.py +295 -49
- package/dist/commands/execute/agent-prompt.d.mts +0 -1
- package/dist/commands/execute/agent-prompt.mjs +0 -24
- package/dist/commands/execute/agent-prompt.mjs.map +0 -1
- package/dist/commands/execute/codex-dispatch.d.mts +0 -3
- package/dist/commands/execute/codex-dispatch.mjs +0 -6
- package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
- package/dist/commands/execute/codex-run.d.mts +0 -3
- package/dist/commands/execute/codex-run.mjs +0 -62
- package/dist/commands/execute/codex-run.mjs.map +0 -1
- package/dist/commands/execute/convergence.d.mts +0 -1
- package/dist/commands/execute/convergence.mjs +0 -38
- package/dist/commands/execute/convergence.mjs.map +0 -1
- package/dist/commands/execute/error-log.d.mts +0 -1
- package/dist/commands/execute/error-log.mjs +0 -18
- package/dist/commands/execute/error-log.mjs.map +0 -1
- package/dist/commands/execute/git-reconcile.d.mts +0 -1
- package/dist/commands/execute/git-reconcile.mjs +0 -30
- package/dist/commands/execute/git-reconcile.mjs.map +0 -1
- package/dist/commands/execute/handoff.d.mts +0 -1
- package/dist/commands/execute/handoff.mjs +0 -31
- package/dist/commands/execute/handoff.mjs.map +0 -1
- package/dist/commands/execute/incremental-carry.d.mts +0 -1
- package/dist/commands/execute/incremental-carry.mjs +0 -20
- package/dist/commands/execute/incremental-carry.mjs.map +0 -1
- package/dist/commands/execute/incremental-scope.d.mts +0 -1
- package/dist/commands/execute/incremental-scope.mjs +0 -29
- package/dist/commands/execute/incremental-scope.mjs.map +0 -1
- package/dist/commands/execute/integrate-stages.d.mts +0 -1
- package/dist/commands/execute/integrate-stages.mjs +0 -24
- package/dist/commands/execute/integrate-stages.mjs.map +0 -1
- package/dist/commands/execute/pane-title.d.mts +0 -1
- package/dist/commands/execute/pane-title.mjs +0 -20
- package/dist/commands/execute/pane-title.mjs.map +0 -1
- package/dist/commands/execute/plan-items.d.mts +0 -1
- package/dist/commands/execute/plan-items.mjs +0 -9
- package/dist/commands/execute/plan-items.mjs.map +0 -1
- package/dist/commands/execute/plan-validate.d.mts +0 -1
- package/dist/commands/execute/plan-validate.mjs +0 -68
- package/dist/commands/execute/plan-validate.mjs.map +0 -1
- package/dist/commands/execute/plan-verify.d.mts +0 -1
- package/dist/commands/execute/plan-verify.mjs +0 -43
- package/dist/commands/execute/plan-verify.mjs.map +0 -1
- package/dist/commands/execute/spawn-followups.d.mts +0 -1
- package/dist/commands/execute/spawn-followups.mjs +0 -22
- package/dist/commands/execute/spawn-followups.mjs.map +0 -1
- package/dist/commands/execute/team.d.mts +0 -3
- package/dist/commands/execute/team.mjs +0 -66
- package/dist/commands/execute/team.mjs.map +0 -1
- package/dist/commands/execute/token-usage.d.mts +0 -1
- package/dist/commands/execute/token-usage.mjs +0 -19
- package/dist/commands/execute/token-usage.mjs.map +0 -1
- package/dist/commands/execute/worker-audit-check.d.mts +0 -1
- package/dist/commands/execute/worker-audit-check.mjs +0 -34
- package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
- package/dist/commands/execute/worker-dispatch.d.mts +0 -7
- package/dist/commands/execute/worker-dispatch.mjs +0 -64
- package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
- package/dist/commands/execute/worker-state.d.mts +0 -1
- package/dist/commands/execute/worker-state.mjs +0 -28
- package/dist/commands/execute/worker-state.mjs.map +0 -1
- package/dist/commands/execute/worktree-lookup.d.mts +0 -1
- package/dist/commands/execute/worktree-lookup.mjs +0 -92
- package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
- package/dist/commands/execute/worktree-status.d.mts +0 -1
- package/dist/commands/execute/worktree-status.mjs +0 -121
- package/dist/commands/execute/worktree-status.mjs.map +0 -1
- package/dist/commands/inspect/code-review.d.mts +0 -1
- package/dist/commands/inspect/code-review.mjs +0 -32
- package/dist/commands/inspect/code-review.mjs.map +0 -1
- package/dist/commands/inspect/container.d.mts +0 -1
- package/dist/commands/inspect/container.mjs +0 -25
- package/dist/commands/inspect/container.mjs.map +0 -1
- package/dist/commands/inspect/context-cost.d.mts +0 -1
- package/dist/commands/inspect/context-cost.mjs +0 -25
- package/dist/commands/inspect/context-cost.mjs.map +0 -1
- package/dist/commands/inspect/design-prep.d.mts +0 -1
- package/dist/commands/inspect/design-prep.mjs +0 -22
- package/dist/commands/inspect/design-prep.mjs.map +0 -1
- package/dist/commands/inspect/error-report.d.mts +0 -1
- package/dist/commands/inspect/error-report.mjs +0 -25
- package/dist/commands/inspect/error-report.mjs.map +0 -1
- package/dist/commands/inspect/error-zip.d.mts +0 -1
- package/dist/commands/inspect/error-zip.mjs +0 -24
- package/dist/commands/inspect/error-zip.mjs.map +0 -1
- package/dist/commands/inspect/log-report.d.mts +0 -1
- package/dist/commands/inspect/log-report.mjs +0 -26
- package/dist/commands/inspect/log-report.mjs.map +0 -1
- package/dist/commands/inspect/model-io.d.mts +0 -1
- package/dist/commands/inspect/model-io.mjs +0 -25
- package/dist/commands/inspect/model-io.mjs.map +0 -1
- package/dist/commands/inspect/profile-show.d.mts +0 -1
- package/dist/commands/inspect/profile-show.mjs +0 -28
- package/dist/commands/inspect/profile-show.mjs.map +0 -1
- package/dist/commands/inspect/recap.d.mts +0 -1
- package/dist/commands/inspect/recap.mjs +0 -30
- package/dist/commands/inspect/recap.mjs.map +0 -1
- package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
- package/dist/commands/inspect/resolve-task-key.mjs +0 -24
- package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
- package/dist/commands/inspect/rollup.d.mts +0 -1
- package/dist/commands/inspect/rollup.mjs +0 -25
- package/dist/commands/inspect/rollup.mjs.map +0 -1
- package/dist/commands/inspect/run-audit.d.mts +0 -1
- package/dist/commands/inspect/run-audit.mjs +0 -25
- package/dist/commands/inspect/run-audit.mjs.map +0 -1
- package/dist/commands/inspect/set-work-status.d.mts +0 -1
- package/dist/commands/inspect/set-work-status.mjs +0 -29
- package/dist/commands/inspect/set-work-status.mjs.map +0 -1
- package/dist/commands/inspect/stage-map.d.mts +0 -1
- package/dist/commands/inspect/stage-map.mjs +0 -131
- package/dist/commands/inspect/stage-map.mjs.map +0 -1
- package/dist/commands/inspect/task-list.d.mts +0 -1
- package/dist/commands/inspect/task-list.mjs +0 -149
- package/dist/commands/inspect/task-list.mjs.map +0 -1
- package/dist/commands/inspect/task-show.d.mts +0 -1
- package/dist/commands/inspect/task-show.mjs +0 -108
- package/dist/commands/inspect/task-show.mjs.map +0 -1
- package/dist/commands/inspect/time-report.d.mts +0 -1
- package/dist/commands/inspect/time-report.mjs +0 -24
- package/dist/commands/inspect/time-report.mjs.map +0 -1
- package/dist/commands/inspect/usage-report.d.mts +0 -1
- package/dist/commands/inspect/usage-report.mjs +0 -23
- package/dist/commands/inspect/usage-report.mjs.map +0 -1
- package/dist/commands/inspect/user-response.d.mts +0 -1
- package/dist/commands/inspect/user-response.mjs +0 -35
- package/dist/commands/inspect/user-response.mjs.map +0 -1
- package/dist/commands/inspect/worker-liveness.d.mts +0 -1
- package/dist/commands/inspect/worker-liveness.mjs +0 -45
- package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
- package/dist/commands/lifecycle/contract-check.d.mts +0 -1
- package/dist/commands/lifecycle/contract-check.mjs +0 -18
- package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
- package/dist/commands/lifecycle/migrate.d.mts +0 -1
- package/dist/commands/lifecycle/migrate.mjs +0 -30
- package/dist/commands/lifecycle/migrate.mjs.map +0 -1
- package/dist/commands/lifecycle/model.d.mts +0 -1
- package/dist/commands/lifecycle/model.mjs +0 -22
- package/dist/commands/lifecycle/model.mjs.map +0 -1
- package/dist/commands/manager.d.mts +0 -3
- package/dist/commands/manager.mjs +0 -50
- package/dist/commands/manager.mjs.map +0 -1
- package/dist/commands/report/agent-activity.d.mts +0 -1
- package/dist/commands/report/agent-activity.mjs +0 -20
- package/dist/commands/report/agent-activity.mjs.map +0 -1
- package/dist/commands/report/approval-decision.d.mts +0 -1
- package/dist/commands/report/approval-decision.mjs +0 -21
- package/dist/commands/report/approval-decision.mjs.map +0 -1
- package/dist/commands/report/design-snapshot.d.mts +0 -1
- package/dist/commands/report/design-snapshot.mjs +0 -19
- package/dist/commands/report/design-snapshot.mjs.map +0 -1
- package/dist/commands/report/finalize.d.mts +0 -4
- package/dist/commands/report/finalize.mjs +0 -64
- package/dist/commands/report/finalize.mjs.map +0 -1
- package/dist/commands/report/inject-report-index.d.mts +0 -1
- package/dist/commands/report/inject-report-index.mjs +0 -21
- package/dist/commands/report/inject-report-index.mjs.map +0 -1
- package/dist/commands/report/render-final-report.d.mts +0 -1
- package/dist/commands/report/render-final-report.mjs +0 -23
- package/dist/commands/report/render-final-report.mjs.map +0 -1
- package/dist/commands/report/render-views.d.mts +0 -1
- package/dist/commands/report/render-views.mjs +0 -27
- package/dist/commands/report/render-views.mjs.map +0 -1
- package/dist/commands/report/translate.d.mts +0 -1
- package/dist/commands/report/translate.mjs +0 -33
- package/dist/commands/report/translate.mjs.map +0 -1
- package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
- package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
- package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
- package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
- package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
- package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
- package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
- package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
- package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
- package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
- package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
- package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
- package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
- package/runtime/bin/okstra-central.sh +0 -152
- package/runtime/bin/okstra-incremental-carry.py +0 -10
- package/runtime/bin/okstra-incremental-scope.py +0 -10
- package/runtime/bin/okstra-team-reconcile.sh +0 -36
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
- package/runtime/python/okstra_ctl/batch.py +0 -60
- package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
- package/runtime/python/okstra_ctl/container_registry.py +0 -72
- package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
- package/runtime/python/okstra_ctl/resolver.py +0 -54
- package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
- package/runtime/python/okstra_ctl/tmux.py +0 -134
- package/runtime/python/okstra_ctl/worktree.py +0 -1099
|
@@ -38,8 +38,25 @@ class _Node:
|
|
|
38
38
|
self.children: list[_Node] = []
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
_NARRATIVE_SCHEMA_RELATIVE = ("schemas", "report-narrative-v3.0.schema.json")
|
|
42
|
+
|
|
43
|
+
|
|
41
44
|
def _narrative_schema_path() -> Path:
|
|
42
|
-
|
|
45
|
+
"""schemas 를 실제로 가진 루트에서 해소한다.
|
|
46
|
+
|
|
47
|
+
부모 개수를 세면 체크아웃에서만 맞는다 — 설치본은 패키지가
|
|
48
|
+
`~/.okstra/lib/python/` 이고 schemas 는 `~/.okstra/schemas/` 라 한 단계
|
|
49
|
+
어긋나서, 설치본으로 실행할 때 서사 파싱이 스키마 없음으로 죽었다.
|
|
50
|
+
"""
|
|
51
|
+
from .paths import find_asset_root
|
|
52
|
+
|
|
53
|
+
root = find_asset_root(_NARRATIVE_SCHEMA_RELATIVE)
|
|
54
|
+
if root is None:
|
|
55
|
+
raise NarrativeContractError(
|
|
56
|
+
"could not locate report-narrative-v3.0.schema.json. Set OKSTRA_HOME "
|
|
57
|
+
"or run from a checkout that contains schemas/."
|
|
58
|
+
)
|
|
59
|
+
return root.joinpath(*_NARRATIVE_SCHEMA_RELATIVE)
|
|
43
60
|
|
|
44
61
|
|
|
45
62
|
def _narrative_schema() -> dict[str, Any]:
|
|
@@ -50,6 +67,77 @@ def _allowed_top_level() -> frozenset[str]:
|
|
|
50
67
|
return frozenset(_narrative_schema().get("properties", {}))
|
|
51
68
|
|
|
52
69
|
|
|
70
|
+
def allowed_top_level_fields() -> frozenset[str]:
|
|
71
|
+
"""작성자가 최상위에 쓸 수 있는 필드 이름(서사 스키마의 properties)."""
|
|
72
|
+
return _allowed_top_level()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# 완성 리포트 스키마가 작성자 소유 블록 안에서 required 로 거는 기계 소유 필드.
|
|
76
|
+
# `writer_owned_data` 가 떼어내고 `_NESTED_FORBIDDEN` 이 저작을 막는 바로 그
|
|
77
|
+
# 이름들이라, 값 제약을 그대로 쓰면 정상 서사가 전부 "필수 필드 없음" 으로
|
|
78
|
+
# 깨진다. 앞의 둘은 `_NESTED_FORBIDDEN` 에서 파생시켜 두 목록이 갈라지지
|
|
79
|
+
# 않게 한다.
|
|
80
|
+
_MACHINE_OWNED_NESTED = frozenset(
|
|
81
|
+
{path.rsplit(".", 1)[-1] for path in _NESTED_FORBIDDEN} | {"designSurfaceCoverage"}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _without_machine_owned_required(node: Any, *, negated: bool = False) -> Any:
|
|
86
|
+
"""`required` 목록에서 기계 소유 필드 이름만 뺀 스키마 사본.
|
|
87
|
+
|
|
88
|
+
`not` 아래는 건드리지 않는다. 거기서 `required: [designPreparation]` 은
|
|
89
|
+
"이 필드가 있으면 안 된다" 는 뜻이고 서사에는 원래 없으니 이미 만족한다.
|
|
90
|
+
이름을 빼면 `not: {required: []}` 가 되는데, 빈 `required` 는 모든 객체가
|
|
91
|
+
만족하므로 그 `not` 은 항상 거짓이 된다 — 통과해야 할 분기를 통과 불가로
|
|
92
|
+
뒤집는다. 실제로 `direction-invalidated` 계획 분기가 그렇게 죽었다.
|
|
93
|
+
"""
|
|
94
|
+
if isinstance(node, list):
|
|
95
|
+
return [_without_machine_owned_required(item, negated=negated) for item in node]
|
|
96
|
+
if not isinstance(node, dict):
|
|
97
|
+
return node
|
|
98
|
+
result: dict[str, Any] = {}
|
|
99
|
+
for key, value in node.items():
|
|
100
|
+
if key == "required" and isinstance(value, list) and not negated:
|
|
101
|
+
result[key] = [
|
|
102
|
+
name for name in value if name not in _MACHINE_OWNED_NESTED
|
|
103
|
+
]
|
|
104
|
+
else:
|
|
105
|
+
result[key] = _without_machine_owned_required(
|
|
106
|
+
value, negated=negated or key == "not"
|
|
107
|
+
)
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _writer_owned_schema(schema: Mapping[str, Any]) -> dict[str, Any]:
|
|
112
|
+
"""작성자가 쓴 서사에 걸 값 제약 — 완성 리포트 스키마에서 잘라 온다.
|
|
113
|
+
|
|
114
|
+
`report-narrative-v3.0.schema.json` 은 최상위 **이름** 허용목록이고 값은
|
|
115
|
+
전부 `{"type": "object"}` 스텁이다. 그 스텁으로만 검증하면 enum 밖 값과
|
|
116
|
+
빠진 필수 필드가 서사 단계를 통과하고, Phase 7 조립이 완성 리포트 스키마에
|
|
117
|
+
부딪힐 때에야 거절된다 — 작성기 워커가 이미 끝난 뒤라 되돌릴 방법이 없다.
|
|
118
|
+
실측(2026-08-26, stage 10 run 002): `verdictCard.direction` 에 enum 에 없는
|
|
119
|
+
`proceed` 가, `humanSummary` 에 필수 `whyItMatters` 가 빠진 채로 서사가
|
|
120
|
+
통과했고 `report-finalize` 의 `project-activity` 가 exit 1 로 떨어졌다.
|
|
121
|
+
|
|
122
|
+
그래서 이름 소유권은 서사 스키마에서, 값 제약은 넘겨받은 완성 리포트
|
|
123
|
+
스키마에서 가져온다. 두 벌을 만들지 않으므로 드리프트가 생기지 않는다.
|
|
124
|
+
서사는 부분 문서다 — 최상위 `required` 는 싣지 않는다. 이 run 의 task
|
|
125
|
+
블록만 있고 나머지 task-type 블록과 기계 소유 최상위 필드는 없기 때문이다.
|
|
126
|
+
"""
|
|
127
|
+
allowed = _allowed_top_level()
|
|
128
|
+
properties = {
|
|
129
|
+
key: value
|
|
130
|
+
for key, value in schema.get("properties", {}).items()
|
|
131
|
+
if key in allowed
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"additionalProperties": False,
|
|
136
|
+
"properties": _without_machine_owned_required(properties),
|
|
137
|
+
"$defs": _without_machine_owned_required(schema.get("$defs", {})),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
53
141
|
def writer_owned_data(data: Mapping[str, Any]) -> dict[str, Any]:
|
|
54
142
|
"""완성 리포트에서 보고서 작성자 소유 필드만 복사한다."""
|
|
55
143
|
owned = {
|
|
@@ -190,7 +278,7 @@ def _parse_tree(markdown: str) -> _Node:
|
|
|
190
278
|
_append_node(stack, field, item, level, number)
|
|
191
279
|
elif value:
|
|
192
280
|
level = _line_level(value.group("indent"), number)
|
|
193
|
-
_append_value(stack, value.group("value"), level, number)
|
|
281
|
+
_append_value(stack, value.group("value"), level, number, lines)
|
|
194
282
|
else:
|
|
195
283
|
raise NarrativeContractError(
|
|
196
284
|
f"line {number}: unsupported Markdown syntax `{line.strip()}` — "
|
|
@@ -216,9 +304,45 @@ def _append_node(stack: list[_Node], field: Any, item: Any, level: int, number:
|
|
|
216
304
|
stack.append(node)
|
|
217
305
|
|
|
218
306
|
|
|
219
|
-
def
|
|
307
|
+
def _misplaced_value_lines(lines: list[str]) -> int:
|
|
308
|
+
"""자기 라벨보다 두 칸 더 들여쓰지 않은 `> value` 줄의 수.
|
|
309
|
+
|
|
310
|
+
파서는 첫 결함에서 멈추므로 규모를 말하지 못한다 — 129줄이 전부 0열에
|
|
311
|
+
붙은 서술문이 `line 5` 한 건으로 보고됐다(2026-09-02 실측). 여기서는
|
|
312
|
+
직전 라벨(`- **…**` / `- Item N`)의 들여쓰기와 견줘 세기만 한다; 판정은
|
|
313
|
+
여전히 파서의 것이다.
|
|
314
|
+
"""
|
|
315
|
+
misplaced = 0
|
|
316
|
+
label_indent = None
|
|
317
|
+
for line in lines[1:]:
|
|
318
|
+
if not line.strip():
|
|
319
|
+
continue
|
|
320
|
+
match = _FIELD_RE.match(line) or _ITEM_RE.match(line)
|
|
321
|
+
if match:
|
|
322
|
+
label_indent = len(match.group("indent"))
|
|
323
|
+
continue
|
|
324
|
+
value = _VALUE_RE.match(line)
|
|
325
|
+
if value and label_indent is not None:
|
|
326
|
+
if len(value.group("indent")) != label_indent + 2:
|
|
327
|
+
misplaced += 1
|
|
328
|
+
return misplaced
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _append_value(
|
|
332
|
+
stack: list[_Node], value: str, level: int, number: int, lines: list[str] | None = None,
|
|
333
|
+
) -> None:
|
|
220
334
|
if stack[-1].level != level - 1:
|
|
221
|
-
|
|
335
|
+
count = _misplaced_value_lines(lines) if lines is not None else 0
|
|
336
|
+
scale = (
|
|
337
|
+
f"; {count} value line(s) in this file share the defect"
|
|
338
|
+
if count > 1
|
|
339
|
+
else ""
|
|
340
|
+
)
|
|
341
|
+
raise NarrativeContractError(
|
|
342
|
+
f"line {number}: value is outside its field — a `> value` line is "
|
|
343
|
+
"indented exactly two spaces deeper than the `- **Label**` or "
|
|
344
|
+
f"`- Item N` line it belongs to{scale}"
|
|
345
|
+
)
|
|
222
346
|
if stack[-1].children:
|
|
223
347
|
raise NarrativeContractError(f"line {number}: field mixes values and children")
|
|
224
348
|
stack[-1].values.append(value)
|
|
@@ -234,7 +358,37 @@ def _allowed_labels(node: Any, index: SchemaIndex, path: str) -> list[str]:
|
|
|
234
358
|
return sorted(humanise(key) for key in keys)
|
|
235
359
|
|
|
236
360
|
|
|
237
|
-
|
|
361
|
+
class _Defects:
|
|
362
|
+
"""값 단계에서 모은 결함. 첫 건에서 멈추지 않고 문서 전체를 읽는다.
|
|
363
|
+
|
|
364
|
+
파서가 첫 강제 변환 실패에서 예외를 내던 동안, 같은 종류의 결함이 여러
|
|
365
|
+
자리에 있어도 한 자리만 보고돼 작성자가 같은 문서를 회차마다 한 자리씩
|
|
366
|
+
고쳤다(2026-09-03 실측, dev-10626 implementation-option-selection: 소수
|
|
367
|
+
`coveragePercent` 가 랭킹 옵션 둘에 있는데 `rankedOptions[1]` 한 건만).
|
|
368
|
+
스키마 검증(`validate`)은 이미 전건을 모으므로 값 단계도 그렇게 한다.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
def __init__(self) -> None:
|
|
372
|
+
self.messages: list[str] = []
|
|
373
|
+
self.paths: list[str] = []
|
|
374
|
+
|
|
375
|
+
def add(self, path: str, message: str) -> None:
|
|
376
|
+
self.paths.append(path)
|
|
377
|
+
self.messages.append(message)
|
|
378
|
+
|
|
379
|
+
def covers(self, error: str) -> bool:
|
|
380
|
+
"""스키마 검증이 같은 자리(또는 그 아래)에 낸 오류인가 — 값 단계가 이미
|
|
381
|
+
보고한 자리를 두 번 말하지 않는다."""
|
|
382
|
+
location = error.split(": ", 1)[0]
|
|
383
|
+
return any(
|
|
384
|
+
location == path or location.startswith(f"{path}.") or location.startswith(f"{path}[")
|
|
385
|
+
for path in self.paths
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _field_key(
|
|
390
|
+
label: str, node: Any, index: SchemaIndex, path: str, defects: _Defects,
|
|
391
|
+
) -> str | None:
|
|
238
392
|
candidates: dict[str, list[str]] = {}
|
|
239
393
|
for key in index.key_order(node):
|
|
240
394
|
candidates.setdefault(label_key(humanise(key)), []).append(key)
|
|
@@ -247,20 +401,26 @@ def _field_key(label: str, node: Any, index: SchemaIndex, path: str) -> str:
|
|
|
247
401
|
if not matches:
|
|
248
402
|
allowed = _allowed_labels(node, index, path)
|
|
249
403
|
listing = ", ".join(f"`{item}`" for item in allowed) or "(none)"
|
|
250
|
-
|
|
404
|
+
defects.add(
|
|
405
|
+
display_path,
|
|
251
406
|
f"owner=report-writer field `{display_path}` is not an allowed unique "
|
|
252
|
-
f"field; allowed at this position: {listing}"
|
|
407
|
+
f"field; allowed at this position: {listing}",
|
|
253
408
|
)
|
|
409
|
+
return None
|
|
254
410
|
if len(matches) > 1:
|
|
255
411
|
collisions = ", ".join(f"`{key}`" for key in matches)
|
|
256
|
-
|
|
412
|
+
defects.add(
|
|
413
|
+
display_path,
|
|
257
414
|
f"owner=report-writer field `{display_path}` is not an allowed unique "
|
|
258
|
-
f"field; the label matches more than one schema key: {collisions}"
|
|
415
|
+
f"field; the label matches more than one schema key: {collisions}",
|
|
259
416
|
)
|
|
417
|
+
return None
|
|
260
418
|
return matches[0]
|
|
261
419
|
|
|
262
420
|
|
|
263
|
-
def _parse_scalar(
|
|
421
|
+
def _parse_scalar(
|
|
422
|
+
values: list[str], node: Any, index: SchemaIndex, path: str, defects: _Defects,
|
|
423
|
+
) -> Any:
|
|
264
424
|
types = _node_types(node, index)
|
|
265
425
|
text = "\n".join(values)
|
|
266
426
|
if text == EMPTY_MARKER and "null" in types:
|
|
@@ -270,17 +430,21 @@ def _parse_scalar(values: list[str], node: Any, index: SchemaIndex, path: str) -
|
|
|
270
430
|
if "integer" in types:
|
|
271
431
|
try:
|
|
272
432
|
return int(text)
|
|
273
|
-
except ValueError
|
|
274
|
-
|
|
433
|
+
except ValueError:
|
|
434
|
+
defects.add(path, f"{path}: expected integer")
|
|
435
|
+
return text
|
|
275
436
|
if "number" in types:
|
|
276
437
|
try:
|
|
277
438
|
return float(text)
|
|
278
|
-
except ValueError
|
|
279
|
-
|
|
439
|
+
except ValueError:
|
|
440
|
+
defects.add(path, f"{path}: expected number")
|
|
441
|
+
return text
|
|
280
442
|
return text
|
|
281
443
|
|
|
282
444
|
|
|
283
|
-
def _parse_value(
|
|
445
|
+
def _parse_value(
|
|
446
|
+
node: _Node, schema_node: Any, index: SchemaIndex, path: str, defects: _Defects,
|
|
447
|
+
) -> Any:
|
|
284
448
|
types = _node_types(schema_node, index)
|
|
285
449
|
if node.values:
|
|
286
450
|
if node.values == [EMPTY_MARKER] and "null" in types:
|
|
@@ -289,52 +453,70 @@ def _parse_value(node: _Node, schema_node: Any, index: SchemaIndex, path: str) -
|
|
|
289
453
|
return {}
|
|
290
454
|
if node.values == [EMPTY_MARKER] and "array" in types:
|
|
291
455
|
return []
|
|
292
|
-
return _parse_scalar(node.values, schema_node, index, path)
|
|
456
|
+
return _parse_scalar(node.values, schema_node, index, path, defects)
|
|
293
457
|
if "array" in types or all(child.kind == "item" for child in node.children):
|
|
294
|
-
return _parse_array(node, schema_node, index, path)
|
|
295
|
-
return _parse_object(node, schema_node, index, path)
|
|
458
|
+
return _parse_array(node, schema_node, index, path, defects)
|
|
459
|
+
return _parse_object(node, schema_node, index, path, defects)
|
|
296
460
|
|
|
297
461
|
|
|
298
|
-
def _parse_array(
|
|
462
|
+
def _parse_array(
|
|
463
|
+
node: _Node, schema_node: Any, index: SchemaIndex, path: str, defects: _Defects,
|
|
464
|
+
) -> list[Any]:
|
|
299
465
|
if any(child.kind != "item" for child in node.children):
|
|
300
|
-
|
|
466
|
+
defects.add(path, f"{path}: array requires Item rows")
|
|
467
|
+
return []
|
|
301
468
|
positions = [int(child.label) for child in node.children]
|
|
302
469
|
if positions != list(range(1, len(positions) + 1)):
|
|
303
|
-
|
|
470
|
+
defects.add(path, f"{path}: Item numbers must be 1..N")
|
|
471
|
+
return []
|
|
304
472
|
item_schema = index.item(schema_node)
|
|
305
473
|
return [
|
|
306
|
-
_parse_value(child, item_schema, index, f"{path}[{position - 1}]")
|
|
474
|
+
_parse_value(child, item_schema, index, f"{path}[{position - 1}]", defects)
|
|
307
475
|
for position, child in zip(positions, node.children, strict=True)
|
|
308
476
|
]
|
|
309
477
|
|
|
310
478
|
|
|
311
|
-
def _parse_object(
|
|
479
|
+
def _parse_object(
|
|
480
|
+
node: _Node, schema_node: Any, index: SchemaIndex, path: str, defects: _Defects,
|
|
481
|
+
) -> dict[str, Any]:
|
|
312
482
|
result: dict[str, Any] = {}
|
|
313
483
|
for child in node.children:
|
|
314
484
|
if child.kind != "field":
|
|
315
|
-
|
|
316
|
-
|
|
485
|
+
defects.add(path, f"{path}: object requires named fields")
|
|
486
|
+
return result
|
|
487
|
+
key = _field_key(child.label, schema_node, index, path, defects)
|
|
488
|
+
if key is None:
|
|
489
|
+
continue
|
|
317
490
|
child_path = f"{path}.{key}" if path else key
|
|
318
491
|
if child_path in _NESTED_FORBIDDEN:
|
|
319
|
-
|
|
320
|
-
f"owner=report-writer cannot author `{path}.{child.label}`"
|
|
492
|
+
defects.add(
|
|
493
|
+
child_path, f"owner=report-writer cannot author `{path}.{child.label}`"
|
|
321
494
|
)
|
|
495
|
+
continue
|
|
322
496
|
if key in result:
|
|
323
|
-
|
|
497
|
+
defects.add(child_path, f"duplicate field: {child_path}")
|
|
498
|
+
continue
|
|
324
499
|
child_schema = index.child(schema_node, key)
|
|
325
500
|
if not _node_types(child_schema, index):
|
|
326
501
|
child_schema = index.schema_for_key(key)
|
|
327
502
|
result[key] = _parse_value(
|
|
328
|
-
child, child_schema, index, child_path
|
|
503
|
+
child, child_schema, index, child_path, defects
|
|
329
504
|
)
|
|
330
505
|
return result
|
|
331
506
|
|
|
332
507
|
|
|
333
508
|
def parse_narrative(markdown: str, schema: Mapping[str, Any]) -> dict[str, Any]:
|
|
334
|
-
"""Markdown을 작성자 소유 자료로
|
|
509
|
+
"""Markdown을 작성자 소유 자료로 읽고, 소유권 표면과 값 제약을 검증한다.
|
|
510
|
+
|
|
511
|
+
`schema` 는 완성 리포트 스키마다. 이름·타입 해석과 값 검증이 모두 그
|
|
512
|
+
한 벌에서 나온다(`_writer_owned_schema`). 값 단계의 결함(강제 변환·모양·
|
|
513
|
+
허용되지 않는 필드)과 스키마 검증의 결함을 한 예외에 모두 싣는다 — 값
|
|
514
|
+
단계가 보고한 자리는 스키마 검증에서 다시 말하지 않는다.
|
|
515
|
+
"""
|
|
335
516
|
root = _parse_tree(markdown)
|
|
336
517
|
index = SchemaIndex(schema)
|
|
337
|
-
|
|
518
|
+
defects = _Defects()
|
|
519
|
+
result = _parse_object(root, schema, index, "", defects)
|
|
338
520
|
unknown = sorted(set(result) - _allowed_top_level())
|
|
339
521
|
if unknown:
|
|
340
522
|
labels = [humanise(key) for key in unknown]
|
|
@@ -345,7 +527,11 @@ def parse_narrative(markdown: str, schema: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
345
527
|
f"owner=report-writer cannot author fields: {labels}; "
|
|
346
528
|
f"writer-owned top-level fields: {owned}"
|
|
347
529
|
)
|
|
348
|
-
errors =
|
|
349
|
-
|
|
350
|
-
|
|
530
|
+
errors = [
|
|
531
|
+
error
|
|
532
|
+
for error in validate(result, _writer_owned_schema(schema))
|
|
533
|
+
if not defects.covers(error)
|
|
534
|
+
]
|
|
535
|
+
if defects.messages or errors:
|
|
536
|
+
raise NarrativeContractError("; ".join(defects.messages + errors))
|
|
351
537
|
return result
|
|
@@ -168,6 +168,9 @@ def _worker_details(team_state: Mapping[str, Any]) -> list[dict[str, Any]]:
|
|
|
168
168
|
usage = worker.get("usage") if isinstance(worker.get("usage"), Mapping) else {}
|
|
169
169
|
row = {"label": _text(worker.get("role") or worker.get("workerId"), "Worker")}
|
|
170
170
|
_populate_usage(row, usage)
|
|
171
|
+
# 작업 시간은 executionStatus 칸이다. workerDetails 스키마는
|
|
172
|
+
# durationMs 를 허용하지 않아 조립이 additionalProperties 로 거절한다.
|
|
173
|
+
row.pop("durationMs", None)
|
|
171
174
|
for key in ("totalTokens", "cacheReadTokens", "billableTokens", "costUsd", "cliTotalTokens", "cliCostUsd"):
|
|
172
175
|
row.setdefault(key, None)
|
|
173
176
|
result.append(row)
|
|
@@ -231,6 +234,9 @@ def project_design(
|
|
|
231
234
|
def _round_history(state: Mapping[str, Any]) -> dict[str, Any]:
|
|
232
235
|
if not (state.get("config") or {}).get("enabled", True):
|
|
233
236
|
return {"disabled": True}
|
|
237
|
+
skip_reason = state.get("round2SkippedReason", "not-skipped")
|
|
238
|
+
if skip_reason == "auto-disabled":
|
|
239
|
+
skip_reason = "single-analyser-only"
|
|
234
240
|
rows = []
|
|
235
241
|
for row in state.get("roundHistory") or []:
|
|
236
242
|
if not isinstance(row, Mapping):
|
|
@@ -243,7 +249,7 @@ def _round_history(state: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
243
249
|
"dispatches": json.dumps(row.get("dispatches") or [], ensure_ascii=False),
|
|
244
250
|
"skippedWorkers": json.dumps(row.get("skippedWorkers") or [], ensure_ascii=False),
|
|
245
251
|
})
|
|
246
|
-
return {"rounds": rows, "round2SkippedReason":
|
|
252
|
+
return {"rounds": rows, "round2SkippedReason": skip_reason}
|
|
247
253
|
|
|
248
254
|
|
|
249
255
|
def _source_items(finding: Mapping[str, Any]) -> list[str]:
|
|
@@ -265,7 +271,7 @@ def project_convergence(state: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
265
271
|
ticket_id = _text(ticket_ids[0] if ticket_ids else "unknown", "unknown")
|
|
266
272
|
if finding.get("classification") in {"full-consensus", "partial-consensus"}:
|
|
267
273
|
row = {
|
|
268
|
-
"id": f"
|
|
274
|
+
"id": f"CV-{len(consensus) + 1:03d}",
|
|
269
275
|
"ticketId": ticket_id,
|
|
270
276
|
"statement": _text(finding.get("summary"), "Converged finding"),
|
|
271
277
|
"sourceItems": _source_items(finding),
|