okstra 0.173.0 → 0.175.1
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 +18 -9
- package/bin/okstra +8 -4
- package/dist/cli-registry.d.mts +16 -0
- package/dist/cli-registry.mjs +560 -0
- package/dist/cli-registry.mjs.map +1 -0
- package/dist/commands/execute/agent-prompt.d.mts +1 -0
- package/{src → dist}/commands/execute/agent-prompt.mjs +10 -11
- package/dist/commands/execute/agent-prompt.mjs.map +1 -0
- package/dist/commands/execute/codex-dispatch.d.mts +3 -0
- package/dist/commands/execute/codex-dispatch.mjs +6 -0
- package/dist/commands/execute/codex-dispatch.mjs.map +1 -0
- package/dist/commands/execute/codex-run.d.mts +3 -0
- package/{src → dist}/commands/execute/codex-run.mjs +30 -37
- package/dist/commands/execute/codex-run.mjs.map +1 -0
- package/dist/commands/execute/convergence.d.mts +1 -0
- package/{src → dist}/commands/execute/convergence.mjs +15 -16
- package/dist/commands/execute/convergence.mjs.map +1 -0
- package/dist/commands/execute/error-log.d.mts +1 -0
- package/{src → dist}/commands/execute/error-log.mjs +2 -3
- package/dist/commands/execute/error-log.mjs.map +1 -0
- package/dist/commands/execute/git-reconcile.d.mts +1 -0
- package/{src → dist}/commands/execute/git-reconcile.mjs +10 -11
- package/dist/commands/execute/git-reconcile.mjs.map +1 -0
- package/dist/commands/execute/handoff.d.mts +1 -0
- package/{src → dist}/commands/execute/handoff.mjs +10 -11
- package/dist/commands/execute/handoff.mjs.map +1 -0
- package/dist/commands/execute/incremental-carry.d.mts +1 -0
- package/{src → dist}/commands/execute/incremental-carry.mjs +2 -3
- package/dist/commands/execute/incremental-carry.mjs.map +1 -0
- package/dist/commands/execute/incremental-scope.d.mts +1 -0
- package/{src → dist}/commands/execute/incremental-scope.mjs +2 -3
- package/dist/commands/execute/incremental-scope.mjs.map +1 -0
- package/dist/commands/execute/integrate-stages.d.mts +1 -0
- package/{src → dist}/commands/execute/integrate-stages.mjs +10 -11
- package/dist/commands/execute/integrate-stages.mjs.map +1 -0
- package/dist/commands/execute/pane-title.d.mts +1 -0
- package/dist/commands/execute/pane-title.mjs +20 -0
- package/dist/commands/execute/pane-title.mjs.map +1 -0
- package/dist/commands/execute/plan-items.d.mts +1 -0
- package/dist/commands/execute/plan-items.mjs +9 -0
- package/dist/commands/execute/plan-items.mjs.map +1 -0
- package/dist/commands/execute/plan-validate.d.mts +1 -0
- package/dist/commands/execute/plan-validate.mjs +68 -0
- package/dist/commands/execute/plan-validate.mjs.map +1 -0
- package/dist/commands/execute/plan-verify.d.mts +1 -0
- package/{src → dist}/commands/execute/plan-verify.mjs +13 -16
- package/dist/commands/execute/plan-verify.mjs.map +1 -0
- package/dist/commands/execute/render-bundle.d.mts +3 -0
- package/{src → dist}/commands/execute/render-bundle.mjs +65 -61
- package/dist/commands/execute/render-bundle.mjs.map +1 -0
- package/dist/commands/execute/run.d.mts +24 -0
- package/dist/commands/execute/run.mjs +196 -0
- package/dist/commands/execute/run.mjs.map +1 -0
- package/dist/commands/execute/spawn-followups.d.mts +1 -0
- package/{src → dist}/commands/execute/spawn-followups.mjs +6 -7
- package/dist/commands/execute/spawn-followups.mjs.map +1 -0
- package/dist/commands/execute/team.d.mts +3 -0
- package/dist/commands/execute/team.mjs +56 -0
- package/dist/commands/execute/team.mjs.map +1 -0
- package/dist/commands/execute/token-usage.d.mts +1 -0
- package/{src → dist}/commands/execute/token-usage.mjs +2 -3
- package/dist/commands/execute/token-usage.mjs.map +1 -0
- package/dist/commands/execute/wizard.d.mts +8 -0
- package/dist/commands/execute/wizard.mjs +191 -0
- package/dist/commands/execute/wizard.mjs.map +1 -0
- package/dist/commands/execute/worker-audit-check.d.mts +1 -0
- package/{src → dist}/commands/execute/worker-audit-check.mjs +11 -12
- package/dist/commands/execute/worker-audit-check.mjs.map +1 -0
- package/dist/commands/execute/worker-dispatch.d.mts +7 -0
- package/{src → dist}/commands/execute/worker-dispatch.mjs +34 -46
- package/dist/commands/execute/worker-dispatch.mjs.map +1 -0
- package/dist/commands/execute/worker-state.d.mts +1 -0
- package/{src → dist}/commands/execute/worker-state.mjs +15 -16
- package/dist/commands/execute/worker-state.mjs.map +1 -0
- package/dist/commands/execute/worktree-lookup.d.mts +1 -0
- package/{src → dist}/commands/execute/worktree-lookup.mjs +38 -37
- package/dist/commands/execute/worktree-lookup.mjs.map +1 -0
- package/dist/commands/execute/worktree-status.d.mts +1 -0
- package/dist/commands/execute/worktree-status.mjs +115 -0
- package/dist/commands/execute/worktree-status.mjs.map +1 -0
- package/dist/commands/inspect/code-review.d.mts +1 -0
- package/{src → dist}/commands/inspect/code-review.mjs +16 -19
- package/dist/commands/inspect/code-review.mjs.map +1 -0
- package/dist/commands/inspect/container.d.mts +1 -0
- package/{src → dist}/commands/inspect/container.mjs +11 -13
- package/dist/commands/inspect/container.mjs.map +1 -0
- package/dist/commands/inspect/context-cost.d.mts +1 -0
- package/{src → dist}/commands/inspect/context-cost.mjs +11 -13
- package/dist/commands/inspect/context-cost.mjs.map +1 -0
- package/dist/commands/inspect/design-prep.d.mts +1 -0
- package/{src → dist}/commands/inspect/design-prep.mjs +10 -11
- package/dist/commands/inspect/design-prep.mjs.map +1 -0
- package/dist/commands/inspect/error-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/error-report.mjs +11 -13
- package/dist/commands/inspect/error-report.mjs.map +1 -0
- package/dist/commands/inspect/error-zip.d.mts +1 -0
- package/{src → dist}/commands/inspect/error-zip.mjs +11 -12
- package/dist/commands/inspect/error-zip.mjs.map +1 -0
- package/dist/commands/inspect/log-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/log-report.mjs +11 -13
- package/dist/commands/inspect/log-report.mjs.map +1 -0
- package/dist/commands/inspect/profile-show.d.mts +1 -0
- package/{src → dist}/commands/inspect/profile-show.mjs +10 -11
- package/dist/commands/inspect/profile-show.mjs.map +1 -0
- package/dist/commands/inspect/recap.d.mts +1 -0
- package/{src → dist}/commands/inspect/recap.mjs +11 -13
- package/dist/commands/inspect/recap.mjs.map +1 -0
- package/dist/commands/inspect/resolve-task-key.d.mts +1 -0
- package/{src → dist}/commands/inspect/resolve-task-key.mjs +11 -13
- package/dist/commands/inspect/resolve-task-key.mjs.map +1 -0
- package/dist/commands/inspect/rollup.d.mts +1 -0
- package/{src → dist}/commands/inspect/rollup.mjs +11 -13
- package/dist/commands/inspect/rollup.mjs.map +1 -0
- package/dist/commands/inspect/run-audit.d.mts +1 -0
- package/{src → dist}/commands/inspect/run-audit.mjs +11 -12
- package/dist/commands/inspect/run-audit.mjs.map +1 -0
- package/dist/commands/inspect/set-work-status.d.mts +1 -0
- package/{src → dist}/commands/inspect/set-work-status.mjs +11 -13
- package/dist/commands/inspect/set-work-status.mjs.map +1 -0
- package/dist/commands/inspect/stage-map.d.mts +1 -0
- package/dist/commands/inspect/stage-map.mjs +110 -0
- package/dist/commands/inspect/stage-map.mjs.map +1 -0
- package/dist/commands/inspect/task-list.d.mts +1 -0
- package/dist/commands/inspect/task-list.mjs +103 -0
- package/dist/commands/inspect/task-list.mjs.map +1 -0
- package/dist/commands/inspect/task-show.d.mts +1 -0
- package/dist/commands/inspect/task-show.mjs +108 -0
- package/dist/commands/inspect/task-show.mjs.map +1 -0
- package/dist/commands/inspect/time-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/time-report.mjs +11 -13
- package/dist/commands/inspect/time-report.mjs.map +1 -0
- package/dist/commands/inspect/usage-report.d.mts +1 -0
- package/{src → dist}/commands/inspect/usage-report.mjs +11 -13
- package/dist/commands/inspect/usage-report.mjs.map +1 -0
- package/dist/commands/inspect/user-response.d.mts +1 -0
- package/{src → dist}/commands/inspect/user-response.mjs +10 -11
- package/dist/commands/inspect/user-response.mjs.map +1 -0
- package/dist/commands/inspect/worker-liveness.d.mts +1 -0
- package/{src → dist}/commands/inspect/worker-liveness.mjs +11 -13
- package/dist/commands/inspect/worker-liveness.mjs.map +1 -0
- package/dist/commands/lifecycle/check-project.d.mts +21 -0
- package/dist/commands/lifecycle/check-project.mjs +175 -0
- package/dist/commands/lifecycle/check-project.mjs.map +1 -0
- package/dist/commands/lifecycle/config.d.mts +1 -0
- package/dist/commands/lifecycle/config.mjs +369 -0
- package/dist/commands/lifecycle/config.mjs.map +1 -0
- package/dist/commands/lifecycle/doctor.d.mts +10 -0
- package/dist/commands/lifecycle/doctor.mjs +317 -0
- package/dist/commands/lifecycle/doctor.mjs.map +1 -0
- package/dist/commands/lifecycle/install.d.mts +17 -0
- package/dist/commands/lifecycle/install.mjs +1077 -0
- package/dist/commands/lifecycle/install.mjs.map +1 -0
- package/dist/commands/lifecycle/migrate.d.mts +1 -0
- package/{src → dist}/commands/lifecycle/migrate.mjs +10 -11
- package/dist/commands/lifecycle/migrate.mjs.map +1 -0
- package/dist/commands/lifecycle/model.d.mts +1 -0
- package/dist/commands/lifecycle/model.mjs +22 -0
- package/dist/commands/lifecycle/model.mjs.map +1 -0
- package/dist/commands/lifecycle/paths.d.mts +1 -0
- package/dist/commands/lifecycle/paths.mjs +102 -0
- package/dist/commands/lifecycle/paths.mjs.map +1 -0
- package/dist/commands/lifecycle/preflight.d.mts +1 -0
- package/dist/commands/lifecycle/preflight.mjs +124 -0
- package/dist/commands/lifecycle/preflight.mjs.map +1 -0
- package/dist/commands/lifecycle/setup.d.mts +1 -0
- package/dist/commands/lifecycle/setup.mjs +297 -0
- package/dist/commands/lifecycle/setup.mjs.map +1 -0
- package/dist/commands/lifecycle/uninstall.d.mts +4 -0
- package/dist/commands/lifecycle/uninstall.mjs +262 -0
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -0
- package/dist/commands/manager.d.mts +3 -0
- package/{src → dist}/commands/manager.mjs +25 -32
- package/dist/commands/manager.mjs.map +1 -0
- package/dist/commands/memory/memory.d.mts +1 -0
- package/dist/commands/memory/memory.mjs +569 -0
- package/dist/commands/memory/memory.mjs.map +1 -0
- package/dist/commands/pr/pr.d.mts +33 -0
- package/dist/commands/pr/pr.mjs +296 -0
- package/dist/commands/pr/pr.mjs.map +1 -0
- package/dist/commands/report/agent-activity.d.mts +1 -0
- package/{src → dist}/commands/report/agent-activity.mjs +11 -12
- package/dist/commands/report/agent-activity.mjs.map +1 -0
- package/dist/commands/report/finalize.d.mts +4 -0
- package/dist/commands/report/finalize.mjs +64 -0
- package/dist/commands/report/finalize.mjs.map +1 -0
- package/dist/commands/report/inject-report-index.d.mts +1 -0
- package/{src → dist}/commands/report/inject-report-index.mjs +6 -7
- package/dist/commands/report/inject-report-index.mjs.map +1 -0
- package/dist/commands/report/render-final-report.d.mts +1 -0
- package/{src → dist}/commands/report/render-final-report.mjs +6 -7
- package/dist/commands/report/render-final-report.mjs.map +1 -0
- package/dist/commands/report/render-views.d.mts +1 -0
- package/{src → dist}/commands/report/render-views.mjs +6 -7
- package/dist/commands/report/render-views.mjs.map +1 -0
- package/dist/commands/report/translate.d.mts +1 -0
- package/{src → dist}/commands/report/translate.mjs +6 -7
- package/dist/commands/report/translate.mjs.map +1 -0
- package/dist/lib/helper-scripts.d.mts +3 -0
- package/{src → dist}/lib/helper-scripts.mjs +11 -14
- package/dist/lib/helper-scripts.mjs.map +1 -0
- package/dist/lib/host-registry-client.d.mts +25 -0
- package/dist/lib/host-registry-client.mjs +152 -0
- package/dist/lib/host-registry-client.mjs.map +1 -0
- package/dist/lib/install-assets.d.mts +8 -0
- package/{src → dist}/lib/install-assets.mjs +17 -20
- package/dist/lib/install-assets.mjs.map +1 -0
- package/dist/lib/okstra-dirs.d.mts +4 -0
- package/{src → dist}/lib/okstra-dirs.mjs +3 -6
- package/dist/lib/okstra-dirs.mjs.map +1 -0
- package/dist/lib/paths.d.mts +4 -0
- package/dist/lib/paths.mjs +78 -0
- package/dist/lib/paths.mjs.map +1 -0
- package/dist/lib/proc.d.mts +3 -0
- package/dist/lib/proc.mjs +31 -0
- package/dist/lib/proc.mjs.map +1 -0
- package/dist/lib/python-helper.d.mts +8 -0
- package/dist/lib/python-helper.mjs +123 -0
- package/dist/lib/python-helper.mjs.map +1 -0
- package/dist/lib/runtime-manifest.d.mts +7 -0
- package/dist/lib/runtime-manifest.mjs +73 -0
- package/dist/lib/runtime-manifest.mjs.map +1 -0
- package/dist/lib/skill-catalog.d.mts +4 -0
- package/dist/lib/skill-catalog.mjs +50 -0
- package/dist/lib/skill-catalog.mjs.map +1 -0
- package/dist/lib/types.d.mts +129 -0
- package/dist/lib/types.mjs +2 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/lib/version.d.mts +2 -0
- package/dist/lib/version.mjs +27 -0
- package/dist/lib/version.mjs.map +1 -0
- package/docs/architecture/storage-model.md +17 -3
- package/docs/architecture.md +12 -22
- package/docs/cli.md +29 -8
- package/docs/container.md +1 -1
- package/docs/contributor-change-matrix.md +3 -3
- package/docs/project-structure-overview.md +29 -20
- package/docs/task-process/README.md +3 -1
- package/docs/task-process/implementation-planning.md +2 -2
- package/docs/task-process/implementation.md +1 -1
- package/package.json +11 -5
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +3 -2
- package/runtime/agents/workers/report-writer-worker.md +3 -1
- package/runtime/agents/workers/translator-worker.md +3 -1
- package/runtime/bin/lib/okstra/cli.sh +12 -0
- package/runtime/bin/lib/okstra/globals.sh +4 -1
- package/runtime/bin/lib/okstra/usage.sh +4 -4
- package/runtime/bin/okstra-error-log.py +27 -0
- package/runtime/bin/okstra-provider-exec.py +245 -16
- package/runtime/bin/okstra-trace-cleanup.sh +58 -129
- package/runtime/bin/okstra.sh +10 -0
- package/runtime/prompts/lead/adapters/cmux.md +2 -0
- package/runtime/prompts/lead/convergence.md +27 -18
- package/runtime/prompts/lead/okstra-lead-contract.md +4 -2
- package/runtime/prompts/lead/plan-body-verification.md +13 -6
- package/runtime/prompts/lead/report-writer.md +6 -6
- package/runtime/prompts/lead/team-contract.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +2 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
- package/runtime/prompts/profiles/change-impact-analysis.md +16 -0
- package/runtime/prompts/profiles/error-analysis.md +21 -1
- package/runtime/prompts/profiles/feature-analysis.md +16 -0
- package/runtime/prompts/profiles/final-verification.md +16 -0
- package/runtime/prompts/profiles/implementation-option-selection.md +16 -0
- package/runtime/prompts/profiles/implementation-planning.md +32 -9
- package/runtime/prompts/profiles/implementation.md +17 -1
- package/runtime/prompts/profiles/improvement-discovery.md +16 -0
- package/runtime/prompts/profiles/project-analysis.md +16 -0
- package/runtime/prompts/profiles/release-handoff.md +5 -1
- package/runtime/prompts/profiles/requirements-discovery.md +20 -0
- package/runtime/prompts/wizard/prompts.ko.json +18 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +1 -6
- package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +8 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +8 -0
- package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +25 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +4 -1
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +4 -4
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +79 -11
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +100 -25
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +59 -14
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +55 -9
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +51 -12
- package/runtime/python/okstra_ctl/agent_activity.py +5 -1
- package/runtime/python/okstra_ctl/agent_invocation.py +608 -14
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +249 -50
- package/runtime/python/okstra_ctl/application/resolve_assignment.py +53 -12
- package/runtime/python/okstra_ctl/assignment_environment.py +102 -0
- package/runtime/python/okstra_ctl/assignment_resolver.py +790 -0
- package/runtime/python/okstra_ctl/attempt_evidence.py +342 -0
- package/runtime/python/okstra_ctl/conformance.py +68 -0
- package/runtime/python/okstra_ctl/convergence.py +409 -3
- package/runtime/python/okstra_ctl/convergence_engine.py +248 -15
- package/runtime/python/okstra_ctl/convergence_store.py +170 -1
- package/runtime/python/okstra_ctl/dispatch_core.py +1181 -102
- package/runtime/python/okstra_ctl/dispatch_state.py +835 -22
- package/runtime/python/okstra_ctl/doctor.py +16 -2
- package/runtime/python/okstra_ctl/domain/host.py +32 -1
- package/runtime/python/okstra_ctl/domain/provider.py +212 -6
- package/runtime/python/okstra_ctl/domain/role.py +125 -0
- package/runtime/python/okstra_ctl/domain/wizard/interaction.py +13 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +39 -1
- package/runtime/python/okstra_ctl/domain/worker_role.py +13 -3
- package/runtime/python/okstra_ctl/domain/worker_stream.py +24 -0
- package/runtime/python/okstra_ctl/error_log_write.py +9 -0
- package/runtime/python/okstra_ctl/error_report.py +21 -1
- package/runtime/python/okstra_ctl/execution_identity.py +529 -0
- package/runtime/python/okstra_ctl/execution_manifest.py +1281 -0
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +619 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +5 -4
- package/runtime/python/okstra_ctl/index.py +6 -2
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +43 -3
- package/runtime/python/okstra_ctl/legacy_model_selection.py +490 -0
- package/runtime/python/okstra_ctl/model_cli.py +278 -0
- package/runtime/python/okstra_ctl/model_defaults.py +46 -0
- package/runtime/python/okstra_ctl/model_discovery.py +26 -7
- package/runtime/python/okstra_ctl/model_pool.py +501 -0
- package/runtime/python/okstra_ctl/models.py +47 -27
- package/runtime/python/okstra_ctl/mutation_recovery.py +293 -0
- package/runtime/python/okstra_ctl/pane_reclaim.py +13 -22
- package/runtime/python/okstra_ctl/pane_title.py +151 -0
- package/runtime/python/okstra_ctl/ports/host_model.py +48 -17
- package/runtime/python/okstra_ctl/ports/worker_dispatch.py +4 -0
- package/runtime/python/okstra_ctl/registry/provider_registry.py +8 -4
- package/runtime/python/okstra_ctl/render.py +78 -10
- package/runtime/python/okstra_ctl/render_final_report.py +34 -19
- package/runtime/python/okstra_ctl/report_contract.py +39 -1
- package/runtime/python/okstra_ctl/report_finalize.py +84 -9
- package/runtime/python/okstra_ctl/report_html/render.py +1 -0
- package/runtime/python/okstra_ctl/role_requirements.py +217 -0
- package/runtime/python/okstra_ctl/run.py +1250 -260
- package/runtime/python/okstra_ctl/run_index_row.py +7 -1
- package/runtime/python/okstra_ctl/schema_excerpt.py +1 -1
- package/runtime/python/okstra_ctl/scope_provenance.py +1 -1
- package/runtime/python/okstra_ctl/session.py +69 -12
- package/runtime/python/okstra_ctl/team.py +51 -25
- package/runtime/python/okstra_ctl/time_report.py +38 -4
- package/runtime/python/okstra_ctl/tmux.py +19 -149
- package/runtime/python/okstra_ctl/usage_identity.py +86 -0
- package/runtime/python/okstra_ctl/usage_report.py +15 -3
- package/runtime/python/okstra_ctl/wizard.py +1253 -62
- package/runtime/python/okstra_ctl/worker_audit_ledger.py +68 -65
- package/runtime/python/okstra_ctl/worker_request.py +9 -1
- package/runtime/python/okstra_ctl/worker_runner.py +130 -5
- package/runtime/python/okstra_ctl/workers.py +7 -5
- package/runtime/python/okstra_ctl/worktree.py +69 -3
- package/runtime/python/okstra_ctl/write_policy.py +571 -0
- package/runtime/python/okstra_token_usage/cli.py +1 -1
- package/runtime/python/okstra_token_usage/collect.py +66 -6
- package/runtime/schemas/convergence-groups-v2.0.schema.json +128 -0
- package/runtime/schemas/execution-manifest-v2.schema.json +340 -0
- package/runtime/schemas/final-report-v2.0.schema.json +25 -0
- package/runtime/skills/okstra-run/SKILL.md +9 -9
- package/runtime/skills/okstra-setup/references/project-config.md +11 -0
- package/runtime/templates/report-writer-prompt-preamble.md +2 -0
- package/runtime/templates/reports/final-report-v2.template.md +7 -1
- package/runtime/templates/reports/html/base.template.html +21 -0
- package/runtime/templates/reports/html/i18n/en.json +4 -0
- package/runtime/templates/reports/html/i18n/ko.json +4 -0
- package/runtime/templates/reports/settings.template.json +0 -24
- package/runtime/templates/worker-prompt-preamble.md +6 -2
- package/runtime/validators/lib/fixtures.sh +98 -33
- package/runtime/validators/validate-implementation-plan-stages.py +63 -3
- package/runtime/validators/validate-run.py +443 -508
- package/runtime/validators/validate_session_conformance.py +26 -8
- package/runtime/bin/okstra-subagent-reclaim.sh +0 -26
- package/runtime/schemas/final-report-v1.0.schema.json +0 -6366
- package/runtime/templates/reports/final-report.template.md +0 -1258
- package/src/cli-registry.mjs +0 -532
- package/src/commands/execute/codex-dispatch.mjs +0 -10
- package/src/commands/execute/plan-items.mjs +0 -9
- package/src/commands/execute/plan-validate.mjs +0 -68
- package/src/commands/execute/run.mjs +0 -208
- package/src/commands/execute/team.mjs +0 -63
- package/src/commands/execute/wizard.mjs +0 -167
- package/src/commands/inspect/stage-map.mjs +0 -98
- package/src/commands/inspect/task-list.mjs +0 -97
- package/src/commands/inspect/task-show.mjs +0 -100
- package/src/commands/lifecycle/check-project.mjs +0 -187
- package/src/commands/lifecycle/config.mjs +0 -385
- package/src/commands/lifecycle/doctor.mjs +0 -308
- package/src/commands/lifecycle/install.mjs +0 -1172
- package/src/commands/lifecycle/paths.mjs +0 -104
- package/src/commands/lifecycle/preflight.mjs +0 -125
- package/src/commands/lifecycle/setup.mjs +0 -333
- package/src/commands/lifecycle/uninstall.mjs +0 -279
- package/src/commands/memory/memory.mjs +0 -524
- package/src/commands/pr/pr.mjs +0 -261
- package/src/commands/report/finalize.mjs +0 -64
- package/src/lib/host-registry-client.mjs +0 -176
- package/src/lib/paths.mjs +0 -86
- package/src/lib/proc.mjs +0 -31
- package/src/lib/python-helper.mjs +0 -142
- package/src/lib/runtime-manifest.mjs +0 -70
- package/src/lib/skill-catalog.mjs +0 -53
- package/src/lib/version.mjs +0 -20
- /package/{src → dist}/commands/pr/default.md +0 -0
|
@@ -84,6 +84,16 @@ def _write_json(path: Path, payload: dict) -> None:
|
|
|
84
84
|
_write_text(path, json.dumps(payload, indent=2, ensure_ascii=False) + "\n")
|
|
85
85
|
|
|
86
86
|
|
|
87
|
+
def _execution_identity(ctx: Mapping[str, object]) -> dict:
|
|
88
|
+
raw = ctx.get("EXECUTION_IDENTITY_JSON")
|
|
89
|
+
if not isinstance(raw, str) or not raw.strip():
|
|
90
|
+
return {}
|
|
91
|
+
payload = json.loads(raw)
|
|
92
|
+
if not isinstance(payload, dict):
|
|
93
|
+
raise TokenRenderError("EXECUTION_IDENTITY_JSON must contain an object")
|
|
94
|
+
return payload
|
|
95
|
+
|
|
96
|
+
|
|
87
97
|
def _lead_runtime(ctx: dict) -> str:
|
|
88
98
|
return ctx.get("LEAD_RUNTIME", "") or "claude-code"
|
|
89
99
|
|
|
@@ -377,10 +387,14 @@ def _agent_contract(ctx: dict) -> dict:
|
|
|
377
387
|
return parsed
|
|
378
388
|
|
|
379
389
|
|
|
380
|
-
def _executor_contract(ctx: dict) -> dict:
|
|
390
|
+
def _executor_contract(ctx: dict) -> dict | None:
|
|
381
391
|
runner = ctx.get("EXECUTOR_RUNNER", "")
|
|
382
392
|
dispatch_mode = ctx.get("EXECUTOR_DISPATCH_MODE", "")
|
|
383
393
|
host_model_value = ctx.get("EXECUTOR_HOST_MODEL_VALUE") or None
|
|
394
|
+
if not runner and not ctx.get("EXECUTOR_PROVIDER"):
|
|
395
|
+
if ctx.get("TASK_TYPE") == "implementation":
|
|
396
|
+
raise ValueError("implementation executor assignment is required")
|
|
397
|
+
return None
|
|
384
398
|
expected_mode = {
|
|
385
399
|
"native-session": "host-native",
|
|
386
400
|
"cli-wrapper": "worker-dispatch",
|
|
@@ -1326,8 +1340,14 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
1326
1340
|
latest_team_state_relative = pointers["latestTeamStatePath"]
|
|
1327
1341
|
latest_resume_command_relative = pointers["latestResumeCommandPath"]
|
|
1328
1342
|
convergence_block = _build_convergence_block(ctx)
|
|
1343
|
+
execution_identity = _execution_identity(ctx)
|
|
1344
|
+
existing_v2 = (
|
|
1345
|
+
existing.get("schemaVersion") == "2.0"
|
|
1346
|
+
and existing.get("executionIdentityVersion") == 2
|
|
1347
|
+
)
|
|
1348
|
+
writes_v2 = execution_identity.get("executionIdentityVersion") == 2
|
|
1329
1349
|
payload = {
|
|
1330
|
-
"schemaVersion": "1.0",
|
|
1350
|
+
"schemaVersion": "2.0" if existing_v2 or writes_v2 else "1.0",
|
|
1331
1351
|
"projectId": ctx.get("PROJECT_ID", ""),
|
|
1332
1352
|
"taskGroup": ctx.get("TASK_GROUP", ""),
|
|
1333
1353
|
"taskId": ctx.get("TASK_ID", ""),
|
|
@@ -1487,6 +1507,12 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
|
|
|
1487
1507
|
"sessionId": ctx.get("CLAUDE_SESSION_ID", ""),
|
|
1488
1508
|
"resumeCommandPath": ctx.get("CLAUDE_RESUME_COMMAND_RELATIVE_PATH", ""),
|
|
1489
1509
|
}
|
|
1510
|
+
if existing_v2 or writes_v2:
|
|
1511
|
+
existing_index = existing.get("roleExecutionIndex")
|
|
1512
|
+
payload["executionIdentityVersion"] = 2
|
|
1513
|
+
payload["roleExecutionIndex"] = (
|
|
1514
|
+
list(existing_index) if isinstance(existing_index, list) else []
|
|
1515
|
+
)
|
|
1490
1516
|
_write_json(path, payload)
|
|
1491
1517
|
|
|
1492
1518
|
|
|
@@ -1608,8 +1634,9 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
1608
1634
|
for s in str(ctx.get("CONCURRENT_RUN_STAGES", "") or "").split(",")
|
|
1609
1635
|
if s.strip().isdigit()
|
|
1610
1636
|
]
|
|
1637
|
+
execution_identity = _execution_identity(ctx)
|
|
1611
1638
|
payload = {
|
|
1612
|
-
"schemaVersion": "1.0",
|
|
1639
|
+
"schemaVersion": execution_identity.get("schemaVersion", "1.0"),
|
|
1613
1640
|
"okstraVersion": ctx.get("OKSTRA_VERSION", ""),
|
|
1614
1641
|
"projectId": ctx.get("PROJECT_ID", ""),
|
|
1615
1642
|
"taskGroup": ctx.get("TASK_GROUP", ""),
|
|
@@ -1783,6 +1810,7 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
|
|
|
1783
1810
|
)
|
|
1784
1811
|
if _lead_runtime(ctx) == "claude-code":
|
|
1785
1812
|
payload["claudeSessionId"] = ctx.get("CLAUDE_SESSION_ID", "")
|
|
1813
|
+
payload.update(execution_identity)
|
|
1786
1814
|
_guard_invocation_manifest_rewrite(existing_run_manifest, payload)
|
|
1787
1815
|
_write_json(Path(run_manifest_path), payload)
|
|
1788
1816
|
|
|
@@ -1795,6 +1823,11 @@ _MODEL_ASSIGNMENT_KEYS = {
|
|
|
1795
1823
|
"hostRuntime",
|
|
1796
1824
|
"hostModelValue",
|
|
1797
1825
|
}
|
|
1826
|
+
_V2_INVOCATION_IDENTITY_KEYS = {
|
|
1827
|
+
"participantRef",
|
|
1828
|
+
"roleExecutionRef",
|
|
1829
|
+
"dutyId",
|
|
1830
|
+
}
|
|
1798
1831
|
_AGENT_CONTRACT_KEYS = {
|
|
1799
1832
|
"schemaVersion",
|
|
1800
1833
|
"dutyRootPath",
|
|
@@ -1860,11 +1893,8 @@ def _validate_invocation_manifest(payload: Mapping[str, object]) -> None:
|
|
|
1860
1893
|
or not assignments
|
|
1861
1894
|
):
|
|
1862
1895
|
raise ValueError("incomplete invocation manifest")
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
for item in assignments.values()
|
|
1866
|
-
):
|
|
1867
|
-
raise ValueError("incomplete invocation manifest assignments")
|
|
1896
|
+
_invocation_manifest_identity_version(payload)
|
|
1897
|
+
_validate_invocation_assignment_shapes(assignments)
|
|
1868
1898
|
_validate_compatibility_projections(payload, assignments)
|
|
1869
1899
|
for key in (
|
|
1870
1900
|
item
|
|
@@ -1901,13 +1931,17 @@ def _validate_compatibility_projections(
|
|
|
1901
1931
|
workers = payload.get("workerAssignments")
|
|
1902
1932
|
if not isinstance(lead, Mapping) or not isinstance(workers, list):
|
|
1903
1933
|
raise ValueError("incomplete invocation manifest compatibility projection")
|
|
1904
|
-
if _model_projection(lead) !=
|
|
1934
|
+
if _model_projection(lead) != _model_projection_from_assignment(
|
|
1935
|
+
assignments.get("lead")
|
|
1936
|
+
):
|
|
1905
1937
|
raise ValueError("immutable invocation manifest lead projection drift")
|
|
1906
1938
|
for worker in workers:
|
|
1907
1939
|
if not isinstance(worker, Mapping):
|
|
1908
1940
|
raise ValueError("incomplete invocation manifest worker projection")
|
|
1909
1941
|
reference = f"initial/{worker.get('workerId', '')}"
|
|
1910
|
-
if _model_projection(worker) !=
|
|
1942
|
+
if _model_projection(worker) != _model_projection_from_assignment(
|
|
1943
|
+
assignments.get(reference)
|
|
1944
|
+
):
|
|
1911
1945
|
raise ValueError("immutable invocation manifest worker projection drift")
|
|
1912
1946
|
|
|
1913
1947
|
|
|
@@ -1915,6 +1949,40 @@ def _model_projection(value: Mapping[str, object]) -> dict[str, object]:
|
|
|
1915
1949
|
return {key: value.get(key) for key in _MODEL_ASSIGNMENT_KEYS}
|
|
1916
1950
|
|
|
1917
1951
|
|
|
1952
|
+
def _model_projection_from_assignment(value: object) -> dict[str, object] | None:
|
|
1953
|
+
if not isinstance(value, Mapping) or not _MODEL_ASSIGNMENT_KEYS.issubset(value):
|
|
1954
|
+
return None
|
|
1955
|
+
return _model_projection(value)
|
|
1956
|
+
|
|
1957
|
+
|
|
1958
|
+
def _invocation_manifest_identity_version(payload: Mapping[str, object]) -> int:
|
|
1959
|
+
schema = payload.get("schemaVersion")
|
|
1960
|
+
identity = payload.get("executionIdentityVersion")
|
|
1961
|
+
if schema == "2.0" and identity == 2:
|
|
1962
|
+
return 2
|
|
1963
|
+
if schema == "2.0" or identity is not None:
|
|
1964
|
+
raise ValueError("invocation manifest mixes v1 and v2 execution identity")
|
|
1965
|
+
if schema not in (None, 1, "1", "1.0"):
|
|
1966
|
+
raise ValueError("unsupported invocation manifest execution identity version")
|
|
1967
|
+
return 1
|
|
1968
|
+
|
|
1969
|
+
|
|
1970
|
+
def _validate_invocation_assignment_shapes(
|
|
1971
|
+
assignments: Mapping[str, object],
|
|
1972
|
+
) -> None:
|
|
1973
|
+
allowed = {frozenset(_MODEL_ASSIGNMENT_KEYS)}
|
|
1974
|
+
for item in assignments.values():
|
|
1975
|
+
if not isinstance(item, Mapping):
|
|
1976
|
+
raise ValueError("incomplete invocation manifest assignments")
|
|
1977
|
+
keys = set(item)
|
|
1978
|
+
if keys & _V2_INVOCATION_IDENTITY_KEYS:
|
|
1979
|
+
raise ValueError(
|
|
1980
|
+
"invocation assignment duplicates v2 execution identity"
|
|
1981
|
+
)
|
|
1982
|
+
if frozenset(keys) not in allowed:
|
|
1983
|
+
raise ValueError("incomplete invocation manifest assignments")
|
|
1984
|
+
|
|
1985
|
+
|
|
1918
1986
|
def render_timeline(timeline_path: str, ctx: dict) -> None:
|
|
1919
1987
|
task_manifest_path = Path(ctx.get("TASK_MANIFEST_PATH", ""))
|
|
1920
1988
|
task_manifest = {}
|
|
@@ -12,10 +12,10 @@ columns in the Execution Status table, omitted §4 phase-continuation
|
|
|
12
12
|
rows, ad-hoc ``## Index`` sections. Routing everything through one
|
|
13
13
|
template + schema cuts those failure modes to zero.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
Rendering never injects a reader index: the AI-handoff markdown keeps its
|
|
16
|
+
compact fixed order and the task-specific HTML provides human navigation.
|
|
17
|
+
``_inject_index_and_anchors`` survives as a standalone pass over an already
|
|
18
|
+
written markdown file, driven by ``scripts/okstra-inject-report-index.py``.
|
|
19
19
|
|
|
20
20
|
Phase 7 mutation flow: ``okstra-token-usage.py --substitute-data`` fills
|
|
21
21
|
the ``tokenUsage`` and ``executionStatus[].totalTokens`` etc. cells in
|
|
@@ -53,7 +53,11 @@ from okstra_ctl.i18n import I18nError, SUPPORTED_LANGS, load_dictionary, make_ji
|
|
|
53
53
|
from okstra_ctl.md_table import UNESCAPED_PIPE_RE, to_cell_text
|
|
54
54
|
from okstra_ctl.models import UnknownModelError, resolve_model_metadata
|
|
55
55
|
from okstra_ctl.paths import find_asset_root
|
|
56
|
-
from okstra_ctl.report_contract import
|
|
56
|
+
from okstra_ctl.report_contract import (
|
|
57
|
+
CURRENT_REPORT_SCHEMA_VERSION,
|
|
58
|
+
TASK_TYPE_DATA_PROPERTY,
|
|
59
|
+
markdown_template_for,
|
|
60
|
+
)
|
|
57
61
|
from okstra_ctl.report_markdown import ReportSections
|
|
58
62
|
from okstra_ctl.schema_excerpt import excerpt_cut_from_version
|
|
59
63
|
from okstra_ctl.seeding import installed_version
|
|
@@ -61,10 +65,9 @@ from okstra_ctl.usage_cells import format_duration_ms, format_int, format_usd
|
|
|
61
65
|
|
|
62
66
|
|
|
63
67
|
TEMPLATE_BY_SCHEMA_VERSION = {
|
|
64
|
-
"1.0": ("templates", "reports", "final-report.template.md"),
|
|
65
68
|
"2.0": ("templates", "reports", "final-report-v2.template.md"),
|
|
66
69
|
}
|
|
67
|
-
DEFAULT_TEMPLATE_REL = TEMPLATE_BY_SCHEMA_VERSION[
|
|
70
|
+
DEFAULT_TEMPLATE_REL = TEMPLATE_BY_SCHEMA_VERSION[CURRENT_REPORT_SCHEMA_VERSION]
|
|
68
71
|
|
|
69
72
|
TASK_DELIVERABLE_TITLES = {
|
|
70
73
|
"requirements-discovery": "Requirements Discovery",
|
|
@@ -563,6 +566,8 @@ def _ai_markdown_context(data: dict, schema: dict | None) -> dict:
|
|
|
563
566
|
context["md_rest"] = sections.rest
|
|
564
567
|
context["md_has"] = sections.has
|
|
565
568
|
context["md_claim"] = sections.mark_rendered
|
|
569
|
+
context["executionRolesTable"] = render_execution_roles_markdown(data)
|
|
570
|
+
context["executionIdentityVersion"] = data.get("executionIdentityVersion")
|
|
566
571
|
return context
|
|
567
572
|
|
|
568
573
|
|
|
@@ -620,6 +625,23 @@ _OPTIONAL_ANALYSIS_DEFAULTS = (
|
|
|
620
625
|
)
|
|
621
626
|
|
|
622
627
|
|
|
628
|
+
def render_execution_roles_markdown(data: dict) -> str:
|
|
629
|
+
roles = data.get("executionRoles") or []
|
|
630
|
+
if data.get("executionIdentityVersion") != 2:
|
|
631
|
+
return "legacy execution roles"
|
|
632
|
+
lines = ["| Role | Label | Status |", "|---|---|---|"]
|
|
633
|
+
for row in roles:
|
|
634
|
+
lines.append(
|
|
635
|
+
f"| {row.get('role', '')} | {row.get('executionLabel', '')} | "
|
|
636
|
+
f"{row.get('status', '')} |"
|
|
637
|
+
)
|
|
638
|
+
return "\n".join(lines)
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def render_execution_roles_html(data: dict) -> str:
|
|
642
|
+
return render_execution_roles_markdown(data)
|
|
643
|
+
|
|
644
|
+
|
|
623
645
|
def _with_optional_defaults(data: dict) -> dict:
|
|
624
646
|
"""Render context with schema-optional fields filled in.
|
|
625
647
|
|
|
@@ -666,14 +688,7 @@ def render(
|
|
|
666
688
|
|
|
667
689
|
try:
|
|
668
690
|
template = env.get_template(template_path.name)
|
|
669
|
-
|
|
670
|
-
_ai_markdown_context(data, schema)
|
|
671
|
-
if data.get("schemaVersion") == "2.0"
|
|
672
|
-
else _with_optional_defaults(data)
|
|
673
|
-
)
|
|
674
|
-
rendered = template.render(**context)
|
|
675
|
-
if data.get("schemaVersion") == "1.0":
|
|
676
|
-
rendered = _inject_index_and_anchors(rendered, dictionary)
|
|
691
|
+
rendered = template.render(**_ai_markdown_context(data, schema))
|
|
677
692
|
return _ventilate_prose(rendered)
|
|
678
693
|
except I18nError as exc:
|
|
679
694
|
raise FinalReportRenderError(
|
|
@@ -689,10 +704,10 @@ def find_default_template(start: Path | None = None) -> Path:
|
|
|
689
704
|
"""Locate the bundled final-report template.
|
|
690
705
|
|
|
691
706
|
Resolution order:
|
|
692
|
-
1. ``$OKSTRA_HOME/templates/reports/final-report.template.md`` (installed runtime).
|
|
693
|
-
2. ``<repo>/templates/reports/final-report.template.md`` (in-repo dev runs).
|
|
707
|
+
1. ``$OKSTRA_HOME/templates/reports/final-report-v2.template.md`` (installed runtime).
|
|
708
|
+
2. ``<repo>/templates/reports/final-report-v2.template.md`` (in-repo dev runs).
|
|
694
709
|
Repo root is detected by walking up from this file until a
|
|
695
|
-
``templates/reports/final-report.template.md`` exists.
|
|
710
|
+
``templates/reports/final-report-v2.template.md`` exists.
|
|
696
711
|
|
|
697
712
|
Raises ``FinalReportRenderError`` if neither path is present.
|
|
698
713
|
"""
|
|
@@ -701,7 +716,7 @@ def find_default_template(start: Path | None = None) -> Path:
|
|
|
701
716
|
return root.joinpath(*DEFAULT_TEMPLATE_REL)
|
|
702
717
|
|
|
703
718
|
raise FinalReportRenderError(
|
|
704
|
-
"could not locate final-report.template.md. Set OKSTRA_HOME or "
|
|
719
|
+
"could not locate final-report-v2.template.md. Set OKSTRA_HOME or "
|
|
705
720
|
"run from a checkout that contains templates/reports/."
|
|
706
721
|
)
|
|
707
722
|
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
CURRENT_REPORT_SCHEMA_VERSION = "2.0"
|
|
6
|
-
LEGACY_REPORT_SCHEMA_VERSION = "1.0"
|
|
7
6
|
|
|
8
7
|
PUBLIC_REPORT_TASK_TYPES = (
|
|
9
8
|
"requirements-discovery",
|
|
@@ -171,6 +170,45 @@ def html_template_for(task_type: str) -> str:
|
|
|
171
170
|
raise ValueError(f"no task-specific html template for: {task_type}") from exc
|
|
172
171
|
|
|
173
172
|
|
|
173
|
+
def execution_roles_from_manifest(manifest: dict) -> list[dict]:
|
|
174
|
+
"""Copy the exact RoleExecution set. Do not invent rows."""
|
|
175
|
+
if int(manifest.get("executionIdentityVersion") or 0) != 2:
|
|
176
|
+
return []
|
|
177
|
+
roles = []
|
|
178
|
+
for row in manifest.get("roleExecutions") or []:
|
|
179
|
+
if not isinstance(row, dict):
|
|
180
|
+
continue
|
|
181
|
+
roles.append({
|
|
182
|
+
"roleExecutionRef": row.get("roleExecutionRef"),
|
|
183
|
+
"role": row.get("role"),
|
|
184
|
+
"provider": row.get("provider"),
|
|
185
|
+
"modelRef": row.get("modelRef"),
|
|
186
|
+
"ordinal": row.get("ordinal"),
|
|
187
|
+
"executionLabel": row.get("executionLabel"),
|
|
188
|
+
"status": row.get("status"),
|
|
189
|
+
})
|
|
190
|
+
return roles
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def apply_execution_roles(data: dict, manifest: dict) -> dict:
|
|
194
|
+
"""Copy the manifest RoleExecution set onto a final-report payload."""
|
|
195
|
+
updated = dict(data)
|
|
196
|
+
if int(manifest.get("executionIdentityVersion") or 0) != 2:
|
|
197
|
+
return updated
|
|
198
|
+
updated["executionIdentityVersion"] = 2
|
|
199
|
+
updated["executionRoles"] = execution_roles_from_manifest(manifest)
|
|
200
|
+
return updated
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def assert_execution_roles_match_manifest(report: dict, manifest: dict) -> None:
|
|
204
|
+
report_refs = {row.get("roleExecutionRef") for row in report.get("executionRoles") or []}
|
|
205
|
+
manifest_refs = {
|
|
206
|
+
row.get("roleExecutionRef") for row in manifest.get("roleExecutions") or []
|
|
207
|
+
}
|
|
208
|
+
if report_refs != manifest_refs:
|
|
209
|
+
raise ValueError("report executionRoles must equal manifest roleExecutions")
|
|
210
|
+
|
|
211
|
+
|
|
174
212
|
def markdown_template_for(task_type: str) -> str:
|
|
175
213
|
try:
|
|
176
214
|
return TASK_TYPE_MARKDOWN_TEMPLATE[task_type]
|
|
@@ -6,6 +6,18 @@ substitution, html view rendering, follow-up task spawning, and run validation.
|
|
|
6
6
|
The order is load-bearing — rendering before substitution ships `--` token
|
|
7
7
|
cells, and validating before rendering trips the report-views contract.
|
|
8
8
|
|
|
9
|
+
`token-usage` is the one step whose failure does not stop the sequence, which
|
|
10
|
+
deliberately accepts that first state: its input is the lead session log, so a
|
|
11
|
+
refusal there would otherwise delete every artifact the later steps produce.
|
|
12
|
+
The run does not pass in that state. `validators/validate-run.py` re-collects
|
|
13
|
+
when the recorded usage is all zeros against an `unavailable` session source
|
|
14
|
+
(`_needs_token_autofix`) and refuses the run rather than ship zeroed counts
|
|
15
|
+
(`accuracy-failed`); a legacy v1 report is caught earlier still, by its
|
|
16
|
+
unsubstituted `{{...}}` placeholders, which a v2 report never carries because
|
|
17
|
+
its numeric cells are `null` until this step fills them. Substituting the
|
|
18
|
+
tokens on a later retry then leaves the already-rendered html stale for
|
|
19
|
+
`validators/validate-report-views.py`.
|
|
20
|
+
|
|
9
21
|
The translation sidecar is NOT one of these steps. `render-views` overlays it,
|
|
10
22
|
so a non-English run dispatches the translator before this sequence starts —
|
|
11
23
|
after verifying the data.json is English, which is why `check-source` is also
|
|
@@ -26,9 +38,11 @@ from pathlib import Path
|
|
|
26
38
|
from typing import Any, Callable, Mapping, Sequence
|
|
27
39
|
|
|
28
40
|
from .agent_activity import ActivityProjectionError, project_agent_activity
|
|
41
|
+
from .report_contract import apply_execution_roles
|
|
29
42
|
from .dispatch_state import DispatchError, link_agent_dispatch_result
|
|
30
43
|
from .final_report_paths import final_report_data_path, final_report_markdown_path
|
|
31
44
|
from .paths import task_dir, task_manifest_file
|
|
45
|
+
from .session import observe_lead_session
|
|
32
46
|
|
|
33
47
|
|
|
34
48
|
STEP_PROJECT_ACTIVITY = "project-activity"
|
|
@@ -354,7 +368,14 @@ def run_finalize(
|
|
|
354
368
|
before_step: Callable[[str], None] | None = None,
|
|
355
369
|
only: Sequence[str] | None = None,
|
|
356
370
|
) -> dict[str, Any]:
|
|
357
|
-
"""Run the Phase 7 steps in order
|
|
371
|
+
"""Run the Phase 7 steps in contractual order.
|
|
372
|
+
|
|
373
|
+
A non-zero exit stops the sequence, with one exception: ``token-usage``
|
|
374
|
+
defers. Its input is the lead session log — state outside this run — so a
|
|
375
|
+
refusal there is not evidence that the artifacts after it are unwritable.
|
|
376
|
+
The deferred failure becomes the result's ``reason`` when nothing later
|
|
377
|
+
fails, and yields to any later failure, which is the step that actually
|
|
378
|
+
blocked the run.
|
|
358
379
|
|
|
359
380
|
``before_step`` fires immediately before each step is spawned, letting an
|
|
360
381
|
adapter settle state the step will read (the Codex adapter marks the report
|
|
@@ -366,7 +387,9 @@ def run_finalize(
|
|
|
366
387
|
step at full token and wall-clock cost.
|
|
367
388
|
"""
|
|
368
389
|
steps: list[dict[str, Any]] = []
|
|
390
|
+
deferred = ""
|
|
369
391
|
try:
|
|
392
|
+
write_execution_roles(ctx)
|
|
370
393
|
commands = build_commands(ctx)
|
|
371
394
|
except FinalizeError as exc:
|
|
372
395
|
return {"ok": False, "reason": str(exc), "steps": steps}
|
|
@@ -426,14 +449,38 @@ def run_finalize(
|
|
|
426
449
|
)
|
|
427
450
|
steps.append(step_payload(name, command, result))
|
|
428
451
|
if result.returncode != 0:
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
452
|
+
failure = f"{name} failed with exit code {result.returncode}"
|
|
453
|
+
# Token collection reads the lead session log, which lives outside
|
|
454
|
+
# this run. One refusal there (`grandTotalTokens=0`) took the whole
|
|
455
|
+
# run's output with it: html never rendered, follow-ups never
|
|
456
|
+
# spawned, and `validate-run` then blocked on `report-views:
|
|
457
|
+
# missing html artifact`. So this step alone defers instead of
|
|
458
|
+
# stopping the sequence. It hides nothing — the non-zero exit stays
|
|
459
|
+
# in `steps`, `ok` stays False, and the closing `validate-run`
|
|
460
|
+
# still refuses the run.
|
|
461
|
+
if name == STEP_TOKEN_USAGE:
|
|
462
|
+
deferred = failure
|
|
463
|
+
continue
|
|
464
|
+
return {"ok": False, "reason": failure, "steps": steps}
|
|
465
|
+
if deferred:
|
|
466
|
+
return {"ok": False, "reason": deferred, "steps": steps}
|
|
434
467
|
return {"ok": True, "reason": "", "steps": steps}
|
|
435
468
|
|
|
436
469
|
|
|
470
|
+
def write_execution_roles(ctx: FinalizeContext) -> None:
|
|
471
|
+
"""Write the exact manifest role set onto the final-report data.json."""
|
|
472
|
+
if not ctx.data_path.is_file() or not ctx.manifest_path.is_file():
|
|
473
|
+
return
|
|
474
|
+
manifest = _load_manifest(ctx.manifest_path)
|
|
475
|
+
data = _load_manifest(ctx.data_path)
|
|
476
|
+
updated = apply_execution_roles(dict(data), dict(manifest))
|
|
477
|
+
if updated == data:
|
|
478
|
+
return
|
|
479
|
+
tmp = ctx.data_path.with_suffix(ctx.data_path.suffix + ".tmp")
|
|
480
|
+
tmp.write_text(json.dumps(updated, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
481
|
+
tmp.replace(ctx.data_path)
|
|
482
|
+
|
|
483
|
+
|
|
437
484
|
def _link_lead_result_for_validation(ctx: FinalizeContext) -> None:
|
|
438
485
|
"""Link the lead-authored synthesis artifact before Phase 7 validation."""
|
|
439
486
|
manifest = _load_manifest(ctx.manifest_path)
|
|
@@ -546,6 +593,29 @@ def _step_summary_lines(result: Mapping[str, Any]) -> list[str]:
|
|
|
546
593
|
return lines
|
|
547
594
|
|
|
548
595
|
|
|
596
|
+
def _recovery_step_names(result: Mapping[str, Any]) -> list[str]:
|
|
597
|
+
"""The steps a retry has to re-run: every step from the earliest failure on.
|
|
598
|
+
|
|
599
|
+
Naming only the failed steps would prescribe half a recovery. `token-usage`
|
|
600
|
+
defers, so `render-views` already wrote an html view from unsubstituted
|
|
601
|
+
data; substituting the tokens on a retry leaves that view stale
|
|
602
|
+
(`validators/validate-report-views.py` checks `source-sha256` against the
|
|
603
|
+
md body). And a sequence that stopped early never reached `validate-run`,
|
|
604
|
+
which is the step that decides whether the run is shippable. Resuming from
|
|
605
|
+
the earliest failure redoes both while still skipping the prefix that
|
|
606
|
+
succeeded — the saving `--only` exists for.
|
|
607
|
+
"""
|
|
608
|
+
failed = {
|
|
609
|
+
string_value(step.get("name"))
|
|
610
|
+
for step in (result.get("steps") or [])
|
|
611
|
+
if step.get("exitCode") != 0
|
|
612
|
+
}
|
|
613
|
+
for index, name in enumerate(STEP_ORDER):
|
|
614
|
+
if name in failed:
|
|
615
|
+
return list(STEP_ORDER[index:])
|
|
616
|
+
return []
|
|
617
|
+
|
|
618
|
+
|
|
549
619
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
550
620
|
args = _parser().parse_args(argv)
|
|
551
621
|
try:
|
|
@@ -553,6 +623,10 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
553
623
|
except FinalizeError as exc:
|
|
554
624
|
print(f"error: {exc}", file=sys.stderr)
|
|
555
625
|
return 2
|
|
626
|
+
# Phase 7 is the last boundary the lead crosses, and the last chance to add
|
|
627
|
+
# the generations resume and compaction split it into. It runs ahead of the
|
|
628
|
+
# sequence because the token-usage step below reads `leadSessionIds`.
|
|
629
|
+
observe_lead_session(ctx.project_root, ctx.team_state_path)
|
|
556
630
|
result = run_finalize(ctx, only=args.only or None)
|
|
557
631
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
558
632
|
print("finalize steps:", file=sys.stderr)
|
|
@@ -560,10 +634,11 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
560
634
|
print(line, file=sys.stderr)
|
|
561
635
|
if not result["ok"]:
|
|
562
636
|
print(f"error: {result['reason']}", file=sys.stderr)
|
|
563
|
-
|
|
637
|
+
recovery = _recovery_step_names(result)
|
|
638
|
+
if recovery:
|
|
639
|
+
flags = " ".join(f"--only {name}" for name in recovery)
|
|
564
640
|
print(
|
|
565
|
-
"
|
|
566
|
-
f"`--only {result['steps'][-1].get('name')}`",
|
|
641
|
+
f"resume the sequence from the earliest failure with `{flags}`",
|
|
567
642
|
file=sys.stderr,
|
|
568
643
|
)
|
|
569
644
|
return 1
|
|
@@ -155,6 +155,7 @@ def render_v2_html_view(
|
|
|
155
155
|
# Every task type ends with the same run-cost section, so it is bound
|
|
156
156
|
# here rather than in ten view models that would each rebuild it.
|
|
157
157
|
"runUsage": run_usage(data),
|
|
158
|
+
"executionRoles": data.get("executionRoles") or [],
|
|
158
159
|
"css": (root / "html/assets/base.css").read_text(encoding="utf-8"),
|
|
159
160
|
"js": response_js + "\n" + base_js,
|
|
160
161
|
}
|