okstra 0.207.1 → 0.209.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.
Files changed (133) hide show
  1. package/README.md +3 -2
  2. package/dist/cli-registry.mjs +6 -0
  3. package/dist/cli-registry.mjs.map +1 -1
  4. package/dist/commands/execute/render-bundle.mjs +1 -1
  5. package/dist/commands/lifecycle/doctor.mjs +1 -1
  6. package/dist/lib/skill-catalog.mjs +1 -0
  7. package/dist/lib/skill-catalog.mjs.map +1 -1
  8. package/docs/architecture/storage-model.md +14 -0
  9. package/docs/architecture.md +30 -9
  10. package/docs/cli.md +26 -22
  11. package/docs/contributor-change-matrix.md +1 -1
  12. package/docs/project-structure-overview.md +15 -8
  13. package/package.json +1 -1
  14. package/runtime/BUILD.json +2 -2
  15. package/runtime/agents/operations/explain-flow.json +6 -0
  16. package/runtime/bin/lib/okstra/cli.sh +1 -5
  17. package/runtime/bin/lib/okstra/globals.sh +0 -2
  18. package/runtime/bin/lib/okstra/usage.sh +5 -3
  19. package/runtime/bin/okstra.sh +0 -2
  20. package/runtime/prompts/duties/business-flow-investigator.json +14 -0
  21. package/runtime/prompts/lead/context-loader.md +1 -1
  22. package/runtime/prompts/lead/convergence.md +22 -7
  23. package/runtime/prompts/lead/okstra-lead-contract.md +10 -6
  24. package/runtime/prompts/lead/report-writer.md +1 -1
  25. package/runtime/prompts/lead/team-contract.md +12 -17
  26. package/runtime/prompts/profiles/_common-contract.md +3 -3
  27. package/runtime/prompts/wizard/prompts.ko.json +0 -91
  28. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +6 -0
  29. package/runtime/python/okstra_ctl/agent/invocation.py +1 -1
  30. package/runtime/python/okstra_ctl/agent/prompt_cli/batch.py +1 -0
  31. package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +1 -0
  32. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +11 -125
  33. package/runtime/python/okstra_ctl/agent/standalone.py +183 -0
  34. package/runtime/python/okstra_ctl/analysis_packet.py +39 -8
  35. package/runtime/python/okstra_ctl/assignment_resolver.py +7 -1
  36. package/runtime/python/okstra_ctl/brief_frontmatter.py +10 -0
  37. package/runtime/python/okstra_ctl/business_flow/__init__.py +4 -0
  38. package/runtime/python/okstra_ctl/business_flow/cli.py +134 -0
  39. package/runtime/python/okstra_ctl/business_flow/contracts.py +268 -0
  40. package/runtime/python/okstra_ctl/business_flow/engine.py +518 -0
  41. package/runtime/python/okstra_ctl/business_flow/hooks.py +221 -0
  42. package/runtime/python/okstra_ctl/business_flow/invocation.py +170 -0
  43. package/runtime/python/okstra_ctl/business_flow/report.py +49 -0
  44. package/runtime/python/okstra_ctl/business_flow/source.py +206 -0
  45. package/runtime/python/okstra_ctl/business_flow/store.py +388 -0
  46. package/runtime/python/okstra_ctl/cmux.py +21 -16
  47. package/runtime/python/okstra_ctl/convergence.py +173 -2
  48. package/runtime/python/okstra_ctl/convergence_critic_verify_prompt.py +18 -0
  49. package/runtime/python/okstra_ctl/convergence_provenance.py +8 -0
  50. package/runtime/python/okstra_ctl/coverage_census.py +603 -0
  51. package/runtime/python/okstra_ctl/design_surfaces.py +4 -0
  52. package/runtime/python/okstra_ctl/direct_work.py +1 -1
  53. package/runtime/python/okstra_ctl/dispatch_core.py +7 -3
  54. package/runtime/python/okstra_ctl/doctor.py +12 -6
  55. package/runtime/python/okstra_ctl/domain/role.py +1 -0
  56. package/runtime/python/okstra_ctl/group_context.py +5 -4
  57. package/runtime/python/okstra_ctl/legacy_model_selection.py +7 -51
  58. package/runtime/python/okstra_ctl/manager_split.py +4 -1
  59. package/runtime/python/okstra_ctl/model_io/lines.py +1 -24
  60. package/runtime/python/okstra_ctl/model_io/renderers.py +54 -41
  61. package/runtime/python/okstra_ctl/phases/change_impact_analysis/profile.json +1 -1
  62. package/runtime/python/okstra_ctl/phases/change_impact_analysis/profile.md +0 -8
  63. package/runtime/python/okstra_ctl/phases/error_analysis/profile.json +1 -1
  64. package/runtime/python/okstra_ctl/phases/error_analysis/profile.md +6 -8
  65. package/runtime/python/okstra_ctl/phases/feature_analysis/profile.json +1 -1
  66. package/runtime/python/okstra_ctl/phases/feature_analysis/profile.md +0 -8
  67. package/runtime/python/okstra_ctl/phases/final_verification/profile.json +1 -1
  68. package/runtime/python/okstra_ctl/phases/final_verification/profile.md +2 -8
  69. package/runtime/python/okstra_ctl/phases/implementation/boundary.json +1 -1
  70. package/runtime/python/okstra_ctl/phases/implementation/profile.json +1 -1
  71. package/runtime/python/okstra_ctl/phases/implementation/profile.md +0 -6
  72. package/runtime/python/okstra_ctl/phases/implementation/report_assets/implementation-input.template.md +1 -1
  73. package/runtime/python/okstra_ctl/phases/implementation_option_selection/authoring.py +4 -3
  74. package/runtime/python/okstra_ctl/phases/implementation_option_selection/entry.py +1 -14
  75. package/runtime/python/okstra_ctl/phases/implementation_option_selection/profile.json +1 -1
  76. package/runtime/python/okstra_ctl/phases/implementation_option_selection/profile.md +5 -8
  77. package/runtime/python/okstra_ctl/phases/implementation_option_selection/spec.md +3 -3
  78. package/runtime/python/okstra_ctl/phases/implementation_option_selection/validation.py +15 -5
  79. package/runtime/python/okstra_ctl/phases/implementation_planning/authoring.py +9 -1
  80. package/runtime/python/okstra_ctl/phases/implementation_planning/boundary.json +1 -1
  81. package/runtime/python/okstra_ctl/phases/implementation_planning/instructions/plan-body-verification.md +4 -2
  82. package/runtime/python/okstra_ctl/phases/implementation_planning/plan_body.py +25 -9
  83. package/runtime/python/okstra_ctl/phases/implementation_planning/profile.json +1 -1
  84. package/runtime/python/okstra_ctl/phases/implementation_planning/profile.md +7 -10
  85. package/runtime/python/okstra_ctl/phases/improvement_discovery/profile.json +1 -1
  86. package/runtime/python/okstra_ctl/phases/improvement_discovery/profile.md +4 -11
  87. package/runtime/python/okstra_ctl/phases/project_analysis/profile.json +1 -1
  88. package/runtime/python/okstra_ctl/phases/project_analysis/profile.md +0 -8
  89. package/runtime/python/okstra_ctl/phases/release_handoff/profile.md +1 -1
  90. package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +1 -1
  91. package/runtime/python/okstra_ctl/phases/requirements_discovery/profile.json +1 -1
  92. package/runtime/python/okstra_ctl/phases/requirements_discovery/profile.md +14 -8
  93. package/runtime/python/okstra_ctl/phases/requirements_discovery/spec.md +3 -3
  94. package/runtime/python/okstra_ctl/phases/technical_verification/profile.json +1 -1
  95. package/runtime/python/okstra_ctl/phases/technical_verification/profile.md +0 -4
  96. package/runtime/python/okstra_ctl/plan_items.py +1 -1
  97. package/runtime/python/okstra_ctl/render.py +10 -43
  98. package/runtime/python/okstra_ctl/render_final_report.py +3 -0
  99. package/runtime/python/okstra_ctl/report_assembly.py +15 -1
  100. package/runtime/python/okstra_ctl/report_finalize.py +50 -0
  101. package/runtime/python/okstra_ctl/report_html/render.py +3 -0
  102. package/runtime/python/okstra_ctl/report_synthesis_packet.py +1 -2
  103. package/runtime/python/okstra_ctl/run.py +78 -409
  104. package/runtime/python/okstra_ctl/wizard/__init__.py +2 -24
  105. package/runtime/python/okstra_ctl/wizard/cli.py +3 -6
  106. package/runtime/python/okstra_ctl/wizard/confirmation.py +3 -35
  107. package/runtime/python/okstra_ctl/wizard/engine.py +2 -4
  108. package/runtime/python/okstra_ctl/wizard/ids.py +1 -88
  109. package/runtime/python/okstra_ctl/wizard/registry.py +36 -228
  110. package/runtime/python/okstra_ctl/wizard/render.py +2 -2
  111. package/runtime/python/okstra_ctl/wizard/roles.py +1 -3
  112. package/runtime/python/okstra_ctl/wizard/sources.py +9 -40
  113. package/runtime/python/okstra_ctl/wizard/state.py +36 -145
  114. package/runtime/python/okstra_ctl/wizard/statefile.py +27 -128
  115. package/runtime/python/okstra_ctl/wizard/steps_identity.py +22 -10
  116. package/runtime/python/okstra_ctl/wizard/steps_options.py +5 -4
  117. package/runtime/python/okstra_ctl/wizard/steps_roles.py +15 -565
  118. package/runtime/python/okstra_ctl/worker_prompt_policy.py +9 -2
  119. package/runtime/schemas/business-flow-v1.schema.json +847 -0
  120. package/runtime/schemas/convergence-groups-v2.0.schema.json +7 -0
  121. package/runtime/skills/okstra-explain-flow/SKILL.md +42 -0
  122. package/runtime/skills/okstra-inspect/facets/history.md +5 -5
  123. package/runtime/skills/okstra-run/SKILL.md +2 -2
  124. package/runtime/templates/reports/business-flow.template.md +106 -0
  125. package/runtime/templates/reports/html/base.template.html +14 -1
  126. package/runtime/templates/reports/html/business-flow.template.html +31 -0
  127. package/runtime/templates/reports/html/i18n/en.json +1 -0
  128. package/runtime/templates/reports/html/i18n/ko.json +1 -0
  129. package/runtime/templates/worker-prompt-preamble.md +11 -2
  130. package/runtime/validators/checks/validate-prompt-metadata-01.py +10 -10
  131. package/runtime/validators/validate-run.py +70 -21
  132. package/runtime/validators/validate_analysis_report.py +21 -21
  133. package/runtime/python/okstra_ctl/workers.py +0 -133
@@ -63,10 +63,6 @@ while [[ $# -gt 0 ]]; do
63
63
  ASSUME_YES="true"
64
64
  shift
65
65
  ;;
66
- --workers)
67
- WORKERS_OVERRIDE="$(require_option_value --workers "${2-}")"
68
- shift 2
69
- ;;
70
66
  --role-count)
71
67
  ROLE_COUNTS+=("$(require_option_value --role-count "${2-}")")
72
68
  shift 2
@@ -253,7 +249,7 @@ while [[ $# -gt 0 ]]; do
253
249
  printf ' hint: did you mean --task-id?\n' >&2
254
250
  ;;
255
251
  esac
256
- printf ' valid options: --render-only --resume-clarification --yes --workers --lead-provider --lead-model --claude-model --codex-model --antigravity-model --worker-model --report-writer-provider --report-writer-model --lead-runtime --executor --critic --related-tasks --work-category --task-type --project-id --project-root --task-group --task-id --task-brief --directive --base-ref --fix-cycle --clarification-response --selected-direction --task-key --approved-plan --approve --implementation-option --stage --stages --qa-waiver --no-plan-verification -h|--help\n' >&2
252
+ printf ' valid options: --render-only --resume-clarification --yes --role-count --role-model --lead-provider --lead-model --claude-model --codex-model --antigravity-model --worker-model --report-writer-provider --report-writer-model --lead-runtime --executor --critic --related-tasks --work-category --task-type --project-id --project-root --task-group --task-id --task-brief --directive --base-ref --fix-cycle --clarification-response --selected-direction --task-key --approved-plan --approve --implementation-option --stage --stages --qa-waiver --no-plan-verification -h|--help\n' >&2
257
253
  usage
258
254
  exit 1
259
255
  ;;
@@ -20,7 +20,6 @@ ASSUME_YES="false"
20
20
  RESUME_CLARIFICATION_MODE="false"
21
21
  RESUME_SESSION_MODE="false"
22
22
  RESUME_SESSION_ID=""
23
- WORKERS_OVERRIDE=""
24
23
  ROLE_COUNTS=()
25
24
  ROLE_MODELS=()
26
25
  HOST_SESSION_CONTEXT_JSON=""
@@ -169,7 +168,6 @@ ANTIGRAVITY_WORKER_MODEL=""
169
168
  ANTIGRAVITY_WORKER_MODEL_EXECUTION_VALUE=""
170
169
  REPORT_WRITER_MODEL=""
171
170
  REPORT_WRITER_MODEL_EXECUTION_VALUE=""
172
- DEFAULT_WORKERS="claude,codex,report-writer"
173
171
  DISPLAY_COMMAND_NAME="${OKSTRA_COMMAND_NAME:-$(basename "$0")}"
174
172
  DISPLAY_TOOL_NAME="${OKSTRA_TOOL_NAME:-okstra}"
175
173
 
@@ -3,7 +3,7 @@
3
3
  usage() {
4
4
  cat >&2 <<USAGE_EOF
5
5
  usage:
6
- $DISPLAY_COMMAND_NAME [--render-only] [--yes] [--no-plan-verification] --task-type <task-type> [--workers worker1,worker2] [--lead-provider <provider>] [--lead-model <model>] [--worker-model provider=model,...] [--report-writer-provider <provider>] [--report-writer-model <model>] [--lead-runtime <host-id-or-alias>] [--executor claude|codex|antigravity] [--critic off|claude|codex|antigravity|grok|kimi] [--related-tasks taskA,taskB] --project-id <project-id> [--project-root <path>] --task-group <task-group> --task-id <task-id> --task-brief <brief-path> [--directive <directive>] [--fix-cycle <yes|no>]
6
+ $DISPLAY_COMMAND_NAME [--render-only] [--yes] [--no-plan-verification] --task-type <task-type> [--role-count <role>=<N>] [--role-model <role>=<provider/model>] [--lead-provider <provider>] [--lead-model <model>] [--worker-model provider=model,...] [--report-writer-provider <provider>] [--report-writer-model <model>] [--lead-runtime <host-id-or-alias>] [--executor claude|codex|antigravity] [--critic off|claude|codex|antigravity|grok|kimi] [--related-tasks taskA,taskB] --project-id <project-id> [--project-root <path>] --task-group <task-group> --task-id <task-id> --task-brief <brief-path> [--directive <directive>] [--fix-cycle <yes|no>]
7
7
 
8
8
  summary:
9
9
  $DISPLAY_TOOL_NAME prepares a task-keyed instruction bundle. The standalone launcher defaults to an interactive Claude session; in-host skills keep the current registered host session as the native lead.
@@ -92,8 +92,10 @@ options:
92
92
  (--project-id/--task-group/--task-id or --task-key). Mutually
93
93
  exclusive with --clarification-response and --approved-plan.
94
94
  --yes Skip interactive prompting and confirmation. Requires all required arguments.
95
- --workers Comma-separated worker list for this run. Default: claude,codex,report-writer.
96
- Optional read-only providers: antigravity, grok, kimi.
95
+ --role-count Instance count for one profile role (repeatable), e.g. analyser=1.
96
+ Allowed range comes from the phase profile.json; default is its recommended count.
97
+ A single analyser runs without cross-model verification.
98
+ --role-model Model for the next instance of a role (repeatable), e.g. analyser=codex/gpt-6.1-sol.
97
99
  --lead-provider Compatibility assertion for the lead assignment. Must match the selected host adapter's native provider.
98
100
  --lead-model Model for the host-native lead. Default: the selected provider's lead policy.
99
101
  --claude-model Model for Claude worker. Default: OKSTRA_DEFAULT_CLAUDE_MODEL or opus
@@ -188,7 +188,6 @@ okstra execution summary:
188
188
  directive: ${DIRECTIVE:-None}
189
189
  clarification response: ${CLARIFICATION_RESPONSE_PATH:-None}
190
190
  selected direction: ${SELECTED_DIRECTION_PATH:-None}
191
- workers override: ${WORKERS_OVERRIDE:-None}
192
191
  executor (implementation only): ${EXECUTOR_OVERRIDE:-default(claude)}
193
192
  approved plan: ${APPROVED_PLAN_PATH:-None}
194
193
  approve ack (CLI 승인 의사): ${APPROVE_PLAN_ACK}
@@ -221,7 +220,6 @@ PY_ARGS=(
221
220
  )
222
221
  [[ -n "${DIRECTIVE-}" ]] && PY_ARGS+=(--directive "$DIRECTIVE")
223
222
  [[ -n "${FIX_CYCLE-}" ]] && PY_ARGS+=(--fix-cycle "$FIX_CYCLE")
224
- [[ -n "${WORKERS_OVERRIDE-}" ]] && PY_ARGS+=(--workers "$WORKERS_OVERRIDE")
225
223
  for role_count in "${ROLE_COUNTS[@]-}"; do
226
224
  [[ -z "$role_count" ]] && continue
227
225
  PY_ARGS+=(--role-count "$role_count")
@@ -0,0 +1,14 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "id": "business-flow-investigator",
4
+ "roleId": "analyser",
5
+ "responsibilities": ["Explain the product business process for a developer new to it, including rules, states, errors, recovery, related projects, and change impacts. Return structured evidence-backed knowledge."],
6
+ "requiredConduct": ["Read implementation, callers, configuration, test bodies and available existing verification records. Inspect every registered candidate for inbound as well as outbound relationships. Follow indirect calls, events, queues, shared data, file transfers and batch processing. Record every unscanned or inaccessible segment."],
7
+ "decisionPrinciples": ["Use the preserved project-specific source baseline for before/after comparisons. Separate static inspection, existing execution verification, expected changes and unknowns. Preserve contradictory claims and investigate their evidence before proposing an unresolved business-policy question."],
8
+ "authorityAndBoundaries": ["The request explicitly lists the source roots and existing artifacts you may read. Source, Git state and existing artifacts are read-only. Return your result in the final response; the runtime owns publication."],
9
+ "evidenceStandards": ["Cite projectId, relative path, one-based line range and the exact inspected excerpt. Do not infer a deployed behavior from source, an execution result from a test body, or absence of impact from missing access. Claims marked execution-verified also cite an existing verification record."],
10
+ "collaborationContract": ["Consume relevant shared facts with their version and conflict status. Contribute only claims grounded in the supplied source state. Never resolve conflict by recency or suppress either original provenance."],
11
+ "completionCriteria": ["Return the requested JSON object with the complete business flow, inspected candidates, structured claims and evidence, source gaps, risks and step-level changes. Empty contributions are valid when no new business facts were observed."],
12
+ "prohibitions": ["Do not edit source or Git state, execute tests, start services, deploy, migrate data, access uncited external material, or publish your own knowledge/report files. Do not invent source evidence, classify expected behavior as current fact, or describe Okstra agent procedure as the product business process."],
13
+ "blockedStateReporting": ["Identify inaccessible candidates, missing source baselines, uninspected branches and unverifiable execution receipts. Provide a partial explanation rather than claiming complete coverage."]
14
+ }
@@ -25,7 +25,7 @@ Do not open, parse, or infer Okstra-owned task, run, discovery, or active-contex
25
25
 
26
26
  Use the `Instruction set` and `Reference expectations` labels in Run Input as paths for Markdown resources. Read only the resources needed for the current action.
27
27
 
28
- 1. `<instruction-set>/analysis-profile.md` for the task-type rules and required worker block.
28
+ 1. `<instruction-set>/analysis-profile.md` for the task-type rules.
29
29
  2. `<instruction-set>/analysis-packet.md` for the Phase 1 compact input.
30
30
  3. `<instruction-set>/host-orchestration-rules.md` when the launch prompt supplies that path.
31
31
 
@@ -6,6 +6,7 @@
6
6
  - [When to Use](#when-to-use)
7
7
  - [Configuration](#configuration)
8
8
  - [Finding Category](#finding-category)
9
+ - [Coverage census](#coverage-census)
9
10
  - [Convergence Algorithm](#convergence-algorithm)
10
11
  - [Round 0: Parse worker results](#round-0-parse-worker-results)
11
12
  - [Round 1-N: Re-verification Loop (queue-pruned)](#round-1-n-re-verification-loop-queue-pruned)
@@ -52,7 +53,7 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
52
53
  | `verificationMode` | `"lightweight"` | `"lightweight"` or `"full-reanalysis"` |
53
54
  | `adversarial` | phase-aware: `true` for `requirements-discovery` / `error-analysis` / `implementation-option-selection` / `implementation-planning` / `project-analysis` / `feature-analysis` / `change-impact-analysis`, `false` otherwise | When `true`, Phase 5.5 runs in **adversarial mode** (see §"Adversarial Verification Mode"): verifiers actively try to refute each finding, the burden of proof sits on the claim, and `verificationMode` is forced to `"full-reanalysis"` scoped to the finding's cited evidence. Resolved by `scripts/okstra_ctl/render.py` `_build_convergence_block` and recorded in `config.adversarial` of the convergence state artifact. |
54
55
 
55
- **Auto-disable rule (BLOCKING).** Convergence requires ≥2 analyser workers to produce a meaningful consensus tally. When the active profile's `Required workers:` block (see `prompts/profiles/*.md`) resolves to fewer than 2 analyser workers — e.g. `release-handoff` (zero analyser workers, lead-only) — the lead MUST treat `convergence.enabled` as `false` for that run regardless of manifest configuration, skip Phases 5.5 and the plan-body verification round (`plan-body-verification` (the absolute path in **Okstra Runtime Resources**)), and record `finalState: "converged"` with `totalRounds: 0`, `round2SkippedReason: "auto-disabled"`, an empty `roundHistory`, and an explanatory note in `config` (e.g. `"autoDisabled": "fewer-than-two-analysers"`). The plan-body round inherits the same rule via its `gating=false` advisory path.
56
+ **Auto-disable rule (BLOCKING).** Convergence requires ≥2 analyser workers to produce a meaningful consensus tally. When the run's selected roster (`resultContract.workerRoles`) holds fewer than 2 analyser workers — the user selected one analyser, or `release-handoff` (zero analyser workers, lead-only) — the lead MUST treat `convergence.enabled` as `false` for that run regardless of manifest configuration, skip Phase 5.5, and record `finalState: "converged"` with `totalRounds: 0`, `round2SkippedReason: "auto-disabled"`, an empty `roundHistory`, and an explanatory note in `config` (e.g. `"autoDisabled": "fewer-than-two-analysers"`). An `implementation-planning` run with one planner still runs the plan-body verification round; that planner's single vote settles each item (`plan-body-verification` (the absolute path in **Okstra Runtime Resources**), "A one-analyser roster"). **Enforced:** `scripts/okstra_ctl/convergence_engine.py` `seed_working_state` stops with `auto-disabled` below two analysis workers, and `validators/validate-run.py` `single_verifier_advisories` records the missing cross-model check as an advisory.
56
57
 
57
58
  ## Finding Category
58
59
 
@@ -64,6 +65,20 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
64
65
  | `unverified` | Final classification only. Assigned to a finding that reached the last executed round with **every recorded vote** `verification-error` — a terminal non-result dispatch, or no analyser available to vote. Nobody inspected it, so `contested` would state a dispute that never happened. The gap ledger already applies the same rule (§'Gap verification'). | Required |
65
66
  | `worker-unique` | Only the discoverer confirms and ALL other non-error votes are `DISAGREE`. `verification-error` votes are excluded from the tally per §"Worker failure handling in reverify"; a finding where every non-discoverer vote is `verification-error` is carried forward, never classified `worker-unique`. | Required |
66
67
 
68
+ ## Coverage census
69
+
70
+ A phase whose profile declares `Census aspects` gives every analysis worker the same fixed worklist: prepare writes `state/coverage-census-<task-type>-<seq>.json` and renders it into the analysis packet as `## Coverage Census`, and each worker answers every cell in section 6 of its result (`Coverage Verdicts`, worker preamble §"Worker output sections"). The census applies to every analysis worker the user selected, whichever providers they are, and it runs whether or not convergence is enabled.
71
+
72
+ Nothing about the census stops a run. A missing cell, a missing result, a single analyser, or a brief without end-state ids leaves a warning; it never refuses a command, a dispatch, or the report.
73
+
74
+ 1. After the initial batch is collected, run `okstra convergence census-audit --run-manifest <run-manifest> --result <worker>=<result-path>`, one `--result` per analysis worker. It writes `state/coverage-census-audit-<task-type>-<seq>.json` and prints, per worker, how many cells have no usable verdict. A path that does not exist counts as a missing result; the command still exits 0. A run whose profile declares no census prints `"census": "absent"` and writes nothing.
75
+ 2. For each worker the audit lists in `gapfillNeeded`, and for no other, send exactly one gap-fill dispatch. Render its instructions with `okstra convergence census-gapfill-prompt --run-manifest <run-manifest> --worker <worker-id>` and write the output verbatim to the instruction file; it carries only that worker's unjudged cells and why each one counts as unjudged. Materialize it with the worker's own `initial/<worker-id>` assignment ref and audience, `--worker-id <worker-id>`, and `--dispatch-kind census-gapfill`; name the result `worker-results/<worker-id>-worker-census-gapfill-<task-type>-<seq>.md` (the `-worker-` token derives the audit sidecar, as for the critic). Dispatch it like any analysis worker; the gap-fill workers of one run go out as one batch. The gap-fill keeps the analysis contract but is exempt from the initial-prompt equality group, like the critic. **Enforced:** `okstra_ctl.worker_prompt_policy.resolve_prompt_plan`; `census-gapfill-prompt` refuses a worker that already had its gap-fill, because the retry is fixed at one.
76
+ 3. After the gap-fill batch settles, run `census-audit` again with the same `--result` arguments plus `--gapfill <worker>=<gapfill-result-path>` for every worker you sent one. A gap-fill that failed, timed out, or wrote no file is still passed: its path records the failure and its cells stay `unverdicted`. Do not dispatch a second gap-fill and do not re-dispatch the worker's initial analysis for missing cells; the run continues to grouping either way.
77
+ 4. Section 6 is census input, not finding input: it never enters the grouped input, and the findings its `finding` lines point to are grouped from sections 1–5 as usual. A finding the gap-fill raised is cited by its item id like any other; the provenance check also looks for it in the worker's gap-fill result.
78
+ 5. Start Round 0 grouping from the final audit's `cellSuggestions`: the items listed under one cell are the first candidates for one group, and that group's `Cells:` line names the cell. Semantic judgment still decides the group — split items that describe different problems, and group findings outside the census by hand as before. A cell in `disagreements` (one worker `finding`, another `clean`) is an explicit dissent: never merge its finding with an item from a worker that judged the cell `clean`. Left single-source, the finding enters the verification queue and the existing reverify rounds settle it; there is no separate round for census dissent.
79
+ 6. With one analysis worker the audit records `crossCheck: "none"` and still writes that worker's matrix column. The run continues.
80
+ 7. Report assembly reads the final audit: each worker with unverdicted cells gets one `## Missing Information and Risk` row (`source: "census-unverdicted"`). A single-analyser run adds no census row: the audit keeps `crossCheck: "none"` and the census adds no single-analyser warning of its own. `validate-run` repeats the unverdicted cells as `validate-run: advisory` lines, never as blocking failures, and `report-finalize` returns the counts as `censusWarnings`.
81
+
67
82
  ## Convergence Algorithm
68
83
 
69
84
  **Majority definition (BLOCKING).** "Majority" means *strictly greater than half* of the non-error votes for that finding (`verification-error` votes are excluded from both numerator and denominator). Ties — including the 1-AGREE / 1-DISAGREE case in a two-analyser roster — are NOT a majority: in intermediate rounds the finding is **carried forward**; in the final executed round the finding is classified `contested`. In adversarial mode a tie whose DISAGREE carries `counter-evidence` does not carry forward — it is classified `contested` in that round (§"Adversarial Verification Mode"). This rule applies identically to the plan-body verification round (`plan-body-verification` (the absolute path in **Okstra Runtime Resources**)) where the same verdict tokens are reused.
@@ -74,7 +89,7 @@ Configure this in the `convergence` block of `task-manifest.json`. If the block
74
89
 
75
90
  Read the worker result files generated in Phase 4/5 and extract individual findings.
76
91
 
77
- **Convergence scope.** Convergence operates on sections 1–5 of the worker output (the common core, see the worker preamble §"Worker output sections"). Section 6 ("Specialization Lens") is additive worker-specific depth and MUST NOT be fed into the consensus grouping, the verification queue, or the round-N reverify prompts. Carry Section 6 forward into the final report verbatim through the report-writer worker — do not let it inflate `unique` counts or trigger spurious `verification-error` statuses.
92
+ **Convergence scope.** Convergence operates on sections 1–5 of the worker output (the common core, see the worker preamble §"Worker output sections"). Section 7 ("Specialization Lens") is additive worker-specific depth and MUST NOT be fed into the consensus grouping, the verification queue, or the round-N reverify prompts. Carry Section 7 forward into the final report verbatim through the report-writer worker — do not let it inflate `unique` counts or trigger spurious `verification-error` statuses.
78
93
 
79
94
  **Incremental re-verification scope (implementation-planning clarification re-runs).** When the lead's `okstra incremental-scope` decision is `mode == "incremental"` (procedure in `prompts/launch.template.md` §"Clarification Response Carried In"), only findings the lead attributes to a stage in `reverify_stages` enter the verification queue. Findings and plan-item verdicts carried forward for `carry_stages` are NOT re-queued. The report writer preserves those stage rows in its narrative, and `okstra incremental-carry` verifies that they are unchanged before copying their prior verdicts into the convergence-owned plan state. When the decision is `mode == "full"` (the default), every finding enters the queue as usual.
80
95
 
@@ -89,10 +104,10 @@ Read the worker result files generated in Phase 4/5 and extract individual findi
89
104
  - Same semantics but disjoint ticket sets → separate groups (do NOT over-merge across tickets).
90
105
  - Only one worker confirms a finding → one single-source group.
91
106
  4. When grouping is ambiguous, prefer splitting over merging (avoid over-merging). Semantic matching, ticket-set equality, and evidence interpretation remain lead judgments; the engine does not perform fuzzy matching or decide whether evidence is credible.
92
- 5. Author the fixed grouping Markdown accepted by `okstra convergence prepare-groups --run-manifest <run-manifest> --input <grouping.md>`, then run that command. Python owns the artifact identifier, target path, schema version, task identity, run-manifest reference, and every participant reference. Each Markdown group records ticket IDs, origin worker and evidence, discovering workers, source worker item IDs, and optional captured evidence. An analysis sidetrack with no ticket uses an empty `Tickets:` value, never a placeholder. Use the ordered functional roster: finding workers have the `analysis` audience, the report author has `report-writer`, and the lead uses `lead`. A lead source never votes. For `implementation` runs the convergence sources are the verifier-role results only — the executor's result is deliverable evidence, not a convergence source (**Enforced:** `_validate_worker_execution_identity` in `scripts/okstra_ctl/convergence_engine.py` rejects an `implementer` source with `analysis audience source role is not allowed`). Never infer live evidence or functional scope from wording, provider, model, or execution label.
107
+ 5. Author the fixed grouping Markdown accepted by `okstra convergence prepare-groups --run-manifest <run-manifest> --input <grouping.md>`, then run that command. Python owns the artifact identifier, target path, schema version, task identity, run-manifest reference, and every participant reference. Each Markdown group records ticket IDs, origin worker and evidence, discovering workers, source worker item IDs, and optional captured evidence. An analysis sidetrack with no ticket uses an empty `Tickets:` value, never a placeholder. A group that answers coverage-census cells adds one optional line, `Cells: C-…, C-…`, which the command records as `cellRefs`; leave it out for a finding outside the census. Use the ordered functional roster: finding workers have the `analysis` audience, the report author has `report-writer`, and the lead uses `lead`. A lead source never votes. For `implementation` runs the convergence sources are the verifier-role results only — the executor's result is deliverable evidence, not a convergence source (**Enforced:** `_validate_worker_execution_identity` in `scripts/okstra_ctl/convergence_engine.py` rejects an `implementer` source with `analysis audience source role is not allowed`). Never infer live evidence or functional scope from wording, provider, model, or execution label.
93
108
 
94
109
  The command sets each worker's paired `participantRef` and `sourceRoleExecutionRef` from the run manifest's canonical role state. It sets `sourceRoleExecutionRef` to the selected source `RoleExecution` row's `roleExecutionRef`, not that row's `sourceRoleExecutionRef` field.
95
- 6. Do not write a queue or classification in this grouped-input artifact. `okstra convergence seed` classifies Round 0 the same way in both modes: a group whose sources are **two or more distinct role executions** becomes `full-consensus` immediately, and only single-source groups enter the working queue. Independent co-derivation is already cross-verification — the adversarial burden of proof targets single-source claims, not a finding two roles reached on their own. A source is counted once per analysis worker, and one analysis worker is exactly one `sourceRoleExecutionRef` — the same identity the reverify roster uses for independence — so two roles held by one provider count as two and no role can count twice. **Enforced:** `_parse_workers` rejects a duplicate `workerId` and `_validate_worker_execution_identity` rejects a duplicate `sourceRoleExecutionRef`, both in `scripts/okstra_ctl/convergence_engine.py`. Semantic grouping merges provenance only; it does not decide a single-source finding is reliable. Section 6 never enters the grouped input.
110
+ 6. Do not write a queue or classification in this grouped-input artifact. `okstra convergence seed` classifies Round 0 the same way in both modes: a group whose sources are **two or more distinct role executions** becomes `full-consensus` immediately, and only single-source groups enter the working queue. Independent co-derivation is already cross-verification — the adversarial burden of proof targets single-source claims, not a finding two roles reached on their own. A source is counted once per analysis worker, and one analysis worker is exactly one `sourceRoleExecutionRef` — the same identity the reverify roster uses for independence — so two roles held by one provider count as two and no role can count twice. **Enforced:** `_parse_workers` rejects a duplicate `workerId` and `_validate_worker_execution_identity` rejects a duplicate `sourceRoleExecutionRef`, both in `scripts/okstra_ctl/convergence_engine.py`. Semantic grouping merges provenance only; it does not decide a single-source finding is reliable. Section 7 never enters the grouped input.
96
111
 
97
112
  ### Round 1-N: Re-verification Loop (queue-pruned)
98
113
 
@@ -148,7 +163,7 @@ Rules:
148
163
  2. Record one event per failed dispatch via `okstra error-log append-observed --error-type cli-failure --agent <worker> ...` (the worker wrapper does this for wrapper failures; for in-process worker timeouts the lead does it).
149
164
  3. `apply-round` adds the worker to the persisted round's `skippedWorkers[]` with `{worker: <W>, reason: "dispatch-non-result", terminalStatus: <timeout|error|not-run>}`.
150
165
  4. If at least one dispatch was issued and every dispatch terminates as non-result, `apply-round` records the `all-reverify-non-result` stop state. The next `plan-round` returns `action: "finalize"`; record one `contract-violation` event per non-result dispatch.
151
- 5. Section 6 (Specialization Lens) of a worker output is OUT of convergence scope per "Convergence scope" above — its absence is NEVER a `verification-error`.
166
+ 5. Section 7 (Specialization Lens) of a worker output is OUT of convergence scope per "Convergence scope" above — its absence is NEVER a `verification-error`.
152
167
 
153
168
  The engine's classifiers treat `verification-error` as "no usable vote" — it counts neither toward AGREE nor toward DISAGREE and is excluded from both numerator and denominator.
154
169
 
@@ -317,7 +332,7 @@ call specification; it does not prove which bytes the host primitive delivered.
317
332
 
318
333
  ### Sponsorship Optimization
319
334
 
320
- For each persisted round plan, build exactly one prompt per `dispatches[]` row and call `redispatch_worker(assignment, prompt, reason)` once through the selected runtime adapter. The prompt contains exactly that row's `findingIds` in plan order and MUST NOT add, remove, or reorder findings. **Enforced (membership only):** `_validate_reverify_prompt_matches_plan` in `validators/validate-run.py` replays `plan == prompt` as a set — it fails an added or dropped finding. Order is not machine-checked; the engine row is the order of record. This excludes Section 6, every resolved finding, and every finding owned by the receiving origin worker because none can appear in the engine row. **Ownership is compared by `sourceRoleExecutionRef`, not by `participantRef`** — a worker sharing the origin's provider and model in a *different* role is a different role contract, a different duty and a different session, so it stays in the panel (ADR-0017; the same doctrine §"Critic gaps" states for critics). **Enforced:** `_worker_is_independent_from_finding` in `scripts/okstra_ctl/convergence_engine.py`. The assignment, model, prompt path, Result Path, worker-results path, errors paths, and `dispatchKind` come from the current run artifacts. Every reverify is a fresh one-shot session.
335
+ For each persisted round plan, build exactly one prompt per `dispatches[]` row and call `redispatch_worker(assignment, prompt, reason)` once through the selected runtime adapter. The prompt contains exactly that row's `findingIds` in plan order and MUST NOT add, remove, or reorder findings. **Enforced (membership only):** `_validate_reverify_prompt_matches_plan` in `validators/validate-run.py` replays `plan == prompt` as a set — it fails an added or dropped finding. Order is not machine-checked; the engine row is the order of record. This excludes Section 7, every resolved finding, and every finding owned by the receiving origin worker because none can appear in the engine row. **Ownership is compared by `sourceRoleExecutionRef`, not by `participantRef`** — a worker sharing the origin's provider and model in a *different* role is a different role contract, a different duty and a different session, so it stays in the panel (ADR-0017; the same doctrine §"Critic gaps" states for critics). **Enforced:** `_worker_is_independent_from_finding` in `scripts/okstra_ctl/convergence_engine.py`. The assignment, model, prompt path, Result Path, worker-results path, errors paths, and `dispatchKind` come from the current run artifacts. Every reverify is a fresh one-shot session.
321
336
 
322
337
  The persisted round plan is the audit record for batch membership. The lead and adapter do not branch on task type, provider, model identity, classification labels, or their own view of the queue. They dispatch only the engine-returned row through the selected runtime adapter.
323
338
 
@@ -644,7 +659,7 @@ For `runner=native-session`, use only `hostModelValue`; for
644
659
  `enforcementMode=host-native-spec-link-gate` and the metadata path. If the
645
660
  persisted assignment or either model value required by its runner is absent,
646
661
  record `critic-skipped: model-unresolved`; never resolve a replacement model.
647
- Result path: `runs/<task-type>/worker-results/<provider>-worker-critic-<task-type>-<seq>.md`.
662
+ Result path: `runs/<task-type>/worker-results/<provider>-worker-critic-<task-type>-<seq>.md`, where `<seq>` is the run manifest's `runSequencesByCategory.workerResults` — the analysers' result seq, which differs from the run sequence after a run that never dispatched. `okstra convergence critic-verify-prompt` reads the critic result from the latest `ok` attempt of the `critic` dispatch in the run manifest and falls back to this name only when no such attempt is recorded (`_settled_critic_result` in `scripts/okstra_ctl/convergence.py`).
648
663
 
649
664
  **What the generated critic task-instructions file contains.** A critic dispatch
650
665
  is not a reverify dispatch: `dispatchKind = "critic"` keeps
@@ -101,6 +101,8 @@ Every `okstra` command the lead documents cite, grouped by phase, each spelled w
101
101
 
102
102
  | Command | Use when | Procedure |
103
103
  |---|---|---|
104
+ | `okstra convergence census-audit --run-manifest <path> --result <worker>=<path>` | After the initial batch, record each analyser's coverage-census verdicts (one `--result` per analyser); again with `--gapfill <worker>=<path>` after the gap-fill dispatch. Unjudged cells are warnings and never fail the command | `convergence` "Coverage census" |
105
+ | `okstra convergence census-gapfill-prompt --run-manifest <path> --worker <worker>` | Print the one gap-fill instruction for a worker the audit lists in `gapfillNeeded`; dispatch it with `--dispatch-kind census-gapfill` | `convergence` "Coverage census" |
104
106
  | `okstra convergence example --kind <kind>` | Print a valid input example before writing a groups, round-results, or critic-results file | `convergence` "Round 1-N" |
105
107
  | `okstra convergence prepare-groups --run-manifest <path> --input <path>` | Publish Round 0 grouped findings | `convergence` "Round 0" |
106
108
  | `okstra convergence seed --groups <path> --work-state <path> --final-state <path> --migration-dir <dir>` | Create, resume, or recover the working state | `convergence` "Round 0" |
@@ -383,7 +385,7 @@ After context-loader completes, read **only the compact intake files below** in
383
385
  **Mandatory at Phase 1 start (parallel Read, one message):**
384
386
 
385
387
  1. `okstra model-io run-input --run-manifest <run-manifest-path found by context-loader>` — fixed Markdown run identity and scope input
386
- 2. `instruction-set/analysis-profile.md` — needed to pick the right `Required workers:` block and phase rules
388
+ 2. `instruction-set/analysis-profile.md` — phase rules
387
389
  3. `instruction-set/analysis-packet.md` — primary compact input for analysis worker dispatch
388
390
 
389
391
  **Doctrine lazy reads (BLOCKING — read at the round, not at Phase 1):**
@@ -437,7 +439,7 @@ If previous run reports exist, use as historical context only. If discovery meta
437
439
 
438
440
  For `project-analysis`, `feature-analysis`, and `change-impact-analysis`, Lead MUST use the run manifest's immutable pre-dispatch `analysisScopeConfirmation` snapshot as the structured reporter-confirmation evidence before Phase 4 worker dispatch. Its `status` MUST be `complete`; its `taskBriefPath` and `briefSha256` bind that status to the exact brief bytes captured when the run manifest was created. A later edit to the live brief, clarification prose, or inferred consent cannot substitute for this snapshot. `project-analysis` confirms which areas remain shallow; `feature-analysis` confirms the exact feature target and covered flows; `change-impact-analysis` confirms the proposed change, preserved behavior, and dependency boundary.
439
441
 
440
- If that snapshot is incomplete, missing, or malformed, Lead MUST follow the shared Reporter Confirmation Required / Clarification Items contract and stop before dispatch. A scope reduction must be explicitly confirmed in the brief's `## Reporter Confirmations` before a fresh bundle records `analysisScopeConfirmation.status=complete`. Do not dispatch workers and then defer a foreseeable scope conflict to a final HTML question. **Enforcement:** `scripts/okstra_ctl/render.py` records the brief path, reporter-confirmation status, and brief byte digest in the run manifest before the lead can dispatch workers; `validators/validate_analysis_report.py` validates only that immutable snapshot, rejects required-worker execution before a complete snapshot, and recomputes the analysis verdict; `validators/validate-run.py` runs that check only after schema validation succeeds.
442
+ If that snapshot is incomplete, missing, or malformed, Lead MUST follow the shared Reporter Confirmation Required / Clarification Items contract and stop before dispatch. A scope reduction must be explicitly confirmed in the brief's `## Reporter Confirmations` before a fresh bundle records `analysisScopeConfirmation.status=complete`. Do not dispatch workers and then defer a foreseeable scope conflict to a final HTML question. **Enforcement:** `scripts/okstra_ctl/render.py` records the brief path, reporter-confirmation status, and brief byte digest in the run manifest before the lead can dispatch workers; `validators/validate_analysis_report.py` validates only that immutable snapshot, rejects analysis-worker execution before a complete snapshot, and recomputes the analysis verdict; `validators/validate-run.py` runs that check only after schema validation succeeds.
441
443
 
442
444
  ## Phase 2 — Phase 5: Prompt preparation, teammate setup, execution, completion poll
443
445
 
@@ -446,12 +448,12 @@ These phases are governed by [team-contract](./team-contract.md). It is the cano
446
448
  - Worker prompt anchor headers and body composition rules.
447
449
  - The `[Required reading]` clause (analysis-packet primary input for analysis workers, full source files for report-writer, scoped inputs for reverify dispatches).
448
450
  - The `[Error reporting]` clause and the asymmetry between claude-worker and codex/antigravity-worker prompts.
449
- - Worker output contract (sections 1–5 + optional Section 6; the Reading Confirmation block lives in the audit sidecar, never in the worker-results file — the preamble "Reading rules" section is canonical and the validator rejects violations), header standard, terminal statuses, errors-sidecar schema.
451
+ - Worker output contract (sections 1–5, Section 6 Coverage Verdicts when the packet carries a census, optional Section 7; the Reading Confirmation block lives in the audit sidecar, never in the worker-results file — the preamble "Reading rules" section is canonical and the validator rejects violations), header standard, terminal statuses, errors-sidecar schema.
450
452
  - Token-usage tracking conventions.
451
453
 
452
454
  For `final-verification`, Lead persists initial analysis prompts with one shared semantic body. Any genuine run delta appears under exactly one `## Run-specific directive` heading and applies to every selected analysis worker; Lead never shards verification requirements by worker, provider, or model. If the shared directive would exceed 40 nonblank lines, Lead writes it into the instruction set and adds the same reference to `analysis-packet.md` before dispatch. Phase 7 validates the persisted initial analysis prompts through the shared prompt contract before the run can pass.
453
455
 
454
- For `improvement-discovery`, Lead records `## Primary Pass Assignments` in the Phase 1.5 grilling log before worker prompt generation. Enumerate selected analyser worker instances in run-manifest `requiredWorkerRoles` order and rotate them over the resolved lenses in log order; provider and model names never determine assignment position. Every analyser still inspects every resolved lens. Phase 7 recomputes this rotation from the persisted roster and fails a missing, extra, duplicate, out-of-order, or out-of-scope assignment.
456
+ For `improvement-discovery`, Lead records `## Primary Pass Assignments` in the Phase 1.5 grilling log before worker prompt generation. Enumerate selected analyser worker instances in run-manifest `workerRoles` order and rotate them over the resolved lenses in log order; provider and model names never determine assignment position. Every analyser still inspects every resolved lens. Phase 7 recomputes this rotation from the persisted roster and fails a missing, extra, duplicate, out-of-order, or out-of-scope assignment.
455
457
 
456
458
  `Report writer worker` is NOT an analysis worker. Do not dispatch it in Phase 4/5 alongside analysis workers. It is invoked only in Phase 6 — see [report-writer](./report-writer.md).
457
459
 
@@ -546,7 +548,7 @@ If convergence is disabled, `seed`/`finalize` produce the auto-disabled final st
546
548
 
547
549
  ### Authoring ownership (BLOCKING)
548
550
 
549
- If `Report writer worker` is in the selected roster (`recommendedWorkers` / `resultContract.requiredWorkerRoles`), Lead dispatches it to author only `report-writer-narrative-<task-type>-<seq>.md`, its worker-result pointer, and its audit sidecar. The worker may read the complete run context but cannot write `final-report-*.data.json` or another role's ledger. After every required input exists, Phase 7 runs report assembly, which validates the role-owned inputs and atomically publishes the report record once. Phase 7 then renders the human HTML from that record. Contract v2 artifacts remain readable but no new run writes them. **Enforced:** report-writer dispatch completion paths in `scripts/okstra_ctl/dispatch_state.py`, granted artifacts in `scripts/okstra_ctl/dispatch_core.py`, and `scripts/okstra_ctl/report_assembly.py` `assemble_report`.
551
+ If `Report writer worker` is in the selected roster (`recommendedWorkers` / `resultContract.workerRoles`), Lead dispatches it to author only `report-writer-narrative-<task-type>-<seq>.md`, its worker-result pointer, and its audit sidecar. The worker may read the complete run context but cannot write `final-report-*.data.json` or another role's ledger. After every required input exists, Phase 7 runs report assembly, which validates the role-owned inputs and atomically publishes the report record once. Phase 7 then renders the human HTML from that record. Contract v2 artifacts remain readable but no new run writes them. **Enforced:** report-writer dispatch completion paths in `scripts/okstra_ctl/dispatch_state.py`, granted artifacts in `scripts/okstra_ctl/dispatch_core.py`, and `scripts/okstra_ctl/report_assembly.py` `assemble_report`.
550
552
 
551
553
  Before constructing the dispatch prompt, the lead MUST:
552
554
 
@@ -606,6 +608,8 @@ For every other task type:
606
608
 
607
609
  When the host native picker is available and two of those rows could apply, ask with that picker (recommended first). Do not end the turn after the status dump.
608
610
 
611
+ When the `report-finalize` result carries `censusWarnings`, add one line to the reply with the unverdicted coverage-census cell count per worker (`unverdicted`), or name the `auditProblem`. It is a warning: it changes neither the recommendation nor the next command. When it carries `businessFlowWarnings`, add one line naming each failed business-flow execution's `mode` and `error`; the business-flow step is optional, so this is also a warning only.
612
+
609
613
  **Cite run-artifact paths, do not assemble them.** The `report-finalize` result's `reportPaths` carries this run's `humanReport`, `reportRecord`, `teamState`, and `renderFullCopy` command, each already rooted at the project (`.okstra/tasks/<task-group>/<task-id>/runs/...`). Every other run-artifact path the reply cites — the resume command among them — comes from the launch prompt's `## Manifests` / `## Run Paths` lists, which are rooted the same way. A path you compose from a `runs/<task-type>/...` pattern instead is identical across every task of that task-type, so it names no task and does not resolve from the project root either.
610
614
 
611
615
  Write file references as Markdown links with short labels in the Report Language, following the launch prompt's file-link presentation guidance. `reportPaths.markdown` supplies absolute destinations for the report, report record, and team state; preserve those destinations and shorten only their display labels. Put each link on its own line without repeating the path in prose or inserting a line break inside the destination. Apply the same format to worker results, error logs, and briefs: take the project-rooted path from `## Manifests` / `## Run Paths` and prefix the absolute project root, so every link destination is absolute. Relative paths stay in commands, not in link destinations. If the terminal expands links into long paths, offer the host-appropriate copyable file-opening command described in the launch prompt. Markdown alone does not guarantee clickable links in every terminal.
@@ -633,7 +637,7 @@ The run-level error log lives at `<runDir>/logs/errors-<task-type>-<seq>.jsonl`.
633
637
  | Skipping a worker silently | Always record terminal status with reason |
634
638
  | Writing verdict before all workers report | Wait for all results or explicit terminal statuses |
635
639
  | Ignoring task bundle model assignments | Task bundle overrides are canonical |
636
- | Inserting per-worker emphasis sentences ("you focus on X") into dispatch prompts | Send byte-identical dispatch prompts per [team-contract](./team-contract.md) "Dispatch-prompt invariant" — specialization lives in Section 6 of the worker output, not the prompt body |
640
+ | Inserting per-worker emphasis sentences ("you focus on X") into dispatch prompts | Send byte-identical dispatch prompts per [team-contract](./team-contract.md) "Dispatch-prompt invariant" — specialization lives in Section 7 of the worker output, not the prompt body |
637
641
  | Omitting contested or worker-unique findings | All categories must appear in the report |
638
642
  | Running full re-analysis when lightweight suffices | Default lightweight; full only when manifest opts in |
639
643
  | Using `/tmp/*prompt*.txt` for worker prompt persistence | Persist the exact worker prompt to the assigned run-level `prompts/` path |
@@ -93,7 +93,7 @@ This section adds report-specific checks to [okstra-lead-contract](./okstra-lead
93
93
  2. The ledger lives at `runs/<task-type>/state/report-writer-corrections-<task-type>-<seq>-a<N>.json` (schema `schemas/report-writer-corrections-v1.0.schema.json`) and holds one entry per defect: `replace` with the exact replacement value (add `current` when you want it checked), `remove` for an item or optional field, `rewrite` with a `rule` when the writer has to re-author prose. Paths use the validator's grammar (`implementationOptionSelection.rankedOptions[1].coverageSummary.coveragePercent`), so a report-assembly refusal can be copied into the ledger verbatim. Never write an indirect instruction such as `use the schema value`, `use the valid status`, or `fix the enum`: a `replacement` is the literal, and a `rule` names the required outcome. You do not copy allowed enum literals by hand — okstra attaches each `rewrite`'s schema constraint from the frozen schema.
94
94
  3. Run `okstra agent-prompt check-corrections --project-root <root> --run-manifest <path> --corrections <ledger>` until it reports no defect. It applies the ledger to a scratch copy of the base narrative and validates the complete proposed narrative against the writer-owned value schema and the task's semantic validator, listing every defect at once. Validating only the edited field is insufficient because one replacement can select a different schema branch, which is why the check covers the whole narrative.
95
95
  4. When the check reports `mechanical: true` and has corrections, run `okstra agent-prompt apply-corrections` with the same arguments: okstra writes the corrected narrative to `reportNarrativePath` and records a `lead-correction-applied` activity row naming the ledger and its correction ids. This includes validated `replace`, `remove`, `add`, `move`, and derived step counts. No writer dispatch, `record-dispatch`, or `link-result` follows; the roster row's result already exists.
96
- 5. Otherwise materialize the writer prompt with the same `--corrections <ledger>` under a new invocation id and prompt path (retire the first attempt's link with `reject-result` as `plan-body-verification` (the absolute path in **Okstra Runtime Resources**) describes). okstra renders the correction-only field values, evidence, schema constraints, replacement-file contract, application command, and output paths. Put context in the ledger; the initial instruction body is not sent to the correction writer.
96
+ 5. Otherwise materialize the writer prompt with the same `--corrections <ledger>` under a new invocation id and prompt path (when the first attempt's result is linked, retire that link with `reject-result` as `plan-body-verification` (the absolute path in **Okstra Runtime Resources**) describes; a dispatch that ended in `error` has no link, so skip that step). okstra renders the correction-only field values, evidence, schema constraints, replacement-file contract, application command, and output paths. Put context in the ledger; the initial instruction body is not sent to the correction writer.
97
97
 
98
98
  A report-writer materialization without `--corrections` whose narrative already exists and parses is refused before any prompt is written — free-form corrections cannot be checked before the writer runs, and four of six re-runs in the 2026-09-03 measurement were lead instructions that contradicted the authoring contract. Only a narrative whose structure does not parse (line grammar, an unknown top-level field) is re-authored, not corrected: that dispatch needs no ledger, and its body quotes the parser's message. Because re-authoring overwrites the live file in place, okstra copies the existing narrative to `worker-results/<narrative-name>.pre-<invocation-id>.md` at materialization and renders a `## Previous Attempt` section naming that copy (**Enforced:** `_preserve_reauthored_narrative` in `scripts/okstra_ctl/agent/prompt_cli/materialize.py`); the 2026-09-09 dev-10642 run lost a 579-line attempt to a failed in-place re-indent command with no copy to fall back on. A narrative that breaks the line grammar is not a produced artifact: the dispatcher settles that attempt as `required worker artifact is unusable: narrative does not parse: …` and retries it inside the same batch, so you see the parser's message at collection, not at Phase 7 assembly (**Enforced:** `okstra_ctl.dispatch_state.unusable_result_defect`, read by `missing_completion_paths` and the `team await` record path). The synthesis packet's Authoring Contract carries the line grammar itself (`report_narrative.NARRATIVE_GRAMMAR_INSTRUCTIONS`), so a writer that reads only the packet still sees it. Value defects — an id outside its pattern, a value outside its enum, a missing required field — leave the structure readable and are exactly what the ledger fixes; the a3 attempt of the 2026-09-03 run carried twenty `SC-` ids that assembly refused and was still a corrective base.
99
99
 
@@ -5,7 +5,7 @@
5
5
  - When verifying worker team composition and operational rules
6
6
  - When applying model assignment rules
7
7
 
8
- **Not applicable to `release-handoff`** — that profile is lead-only and intentionally has no `Required workers:` block (see `scripts/okstra_ctl/phases/release_handoff/profile.md`). The worker-dispatch contract in this document does not engage during `release-handoff` runs.
8
+ **Not applicable to `release-handoff`** — that profile is lead-only and its `profile.json` declares no roles (see `scripts/okstra_ctl/phases/release_handoff/profile.md`). The worker-dispatch contract in this document does not engage during `release-handoff` runs.
9
9
 
10
10
  ## Team Structure
11
11
 
@@ -13,7 +13,7 @@ Okstra tasks use one lead plus the exact worker assignments selected in the prep
13
13
 
14
14
  ### Role Definitions
15
15
 
16
- The start screen assigns a **model ref** (`provider/model`) to each **canonical role** slot: `leader`, `analyser`, `critic`, `designer`, `planner`, `implementer`, `verifier`, `report-writer`, `translator`. A provider name is the front of a model ref, not a role. Every `analyser` in the run shares an identical core responsibility. Specialization is additive — it lives in optional Section 6 of the worker output, NOT in differentiated core questions. Cross-verification only converges if every rostered analyser answers the same questions against the same brief.
16
+ The start screen assigns a **model ref** (`provider/model`) to each **canonical role** slot: `leader`, `analyser`, `critic`, `designer`, `planner`, `implementer`, `verifier`, `report-writer`, `translator`. A provider name is the front of a model ref, not a role. Every `analyser` in the run shares an identical core responsibility. Specialization is additive — it lives in optional Section 7 of the worker output, NOT in differentiated core questions. Cross-verification only converges if every rostered analyser answers the same questions against the same brief.
17
17
 
18
18
  | Canonical role | Core responsibility | Notes |
19
19
  |------|------|------|
@@ -27,7 +27,7 @@ The start screen assigns a **model ref** (`provider/model`) to each **canonical
27
27
  | `report-writer` | **Authors** the final-report file in Phase 6 | Excluded from Phase 4/5 and convergence |
28
28
  | `translator` | Translates the designated sidecar only | Phase 7 |
29
29
 
30
- **Dispatch does not invent a missing model assignment.** Launch-time empty slots are filled by the model default chain before the run starts. At dispatch the model for every role comes from `resultContract.requiredWorkerRoles[*].modelExecutionValue` in `task-manifest.json` (and lead model metadata). There is no per-role hard-coded fallback — see "Model Assignment Rules" below.
30
+ **Dispatch does not invent a missing model assignment.** Launch-time empty slots are filled by the model default chain before the run starts. At dispatch the model for every role comes from `resultContract.workerRoles[*].modelExecutionValue` in `task-manifest.json` (and lead model metadata). There is no per-role hard-coded fallback — see "Model Assignment Rules" below.
31
31
 
32
32
  **Dispatch-prompt invariant.** Lead's dispatch prompt body for every rostered analysis worker MUST be byte-identical except for the role label and wrapper-specific path headers (for example `**Worktree:**`). The role label is the ONLY identity form the normalizer erases, and it erases exactly the label `worker_prompt_body.analysis_worker_label` renders for the run's own worker ids — so a provider outside that function's display map (`grok`, `kimi`, an installed adapter) is covered as it comes. Naming the worker any other way (a model name, a host name, a provider's product name) survives normalization and fails the equality group before publication. **Enforced:** `okstra_ctl.worker_prompt_contract.normalise_analysis_prompt`, pinned by `tests/contract/test_analysis_prompt_identity_normalization.py`. Lead MUST NOT bias the brief by inserting per-worker emphasis sentences ("you focus on X") into the body. Bias-by-prompt reproduces the historical failure mode where Claude commented only on assumptions, Codex only on code paths, and Antigravity only on requirements — leaving convergence with nothing to converge on.
33
33
 
@@ -35,21 +35,16 @@ Disjoint initial scopes are invalid triangulation. Every selected analysis worke
35
35
 
36
36
  ### Model Assignment Rules
37
37
 
38
- 1. `resultContract.requiredWorkerRoles` in `task-manifest.json` (and the lead model metadata) is the canonical source. There is no role-level fallback — a missing assignment is a manifest defect, not a license to invent one.
38
+ 1. `resultContract.workerRoles` in `task-manifest.json` (and the lead model metadata) is the canonical source. There is no role-level fallback — a missing assignment is a manifest defect, not a license to invent one.
39
39
  2. Select the execution value from `runner`: `native-session` passes only `hostModelValue` to the host primitive, while `cli-wrapper` passes `modelExecutionValue` to the provider process. Both values remain recorded in the invocation contract; neither may be substituted for the other.
40
40
  3. **Dispatch-time enforcement (BLOCKING).** The selected adapter receives the complete assignment and must apply the runner-specific value above. The adapter must fail before dispatch if it cannot apply the exact assignment; it must not inherit the lead model, change provider, or choose a nearby alias silently.
41
41
  **Enforced:** `scripts/okstra_ctl/dispatch_core.py` reads the invocation metadata through `dispatch_state.require_string`, which raises `DispatchError` on a missing or blank `modelExecutionValue` / `assignmentRef`, and rejects a non-string `hostModelValue` — the dispatch stops before the worker is spawned rather than falling back to the lead's model.
42
42
 
43
43
  ### Dynamic Worker Role Determination
44
44
 
45
- **Roster canonical-source rule.** The profile's `Required workers:` block (in `prompts/profiles/<phase>.md`) is the **static roster definition** — the set of roles legal for that phase. `resultContract.requiredWorkerRoles` in `task-manifest.json` is the **per-run instance** — the actual roster materialized for this run, after recommendation, user selection, and any post-recommendation overrides. **On conflict, the task-manifest wins** — it is what the run was actually launched with, and what lead must dispatch against.
45
+ **Roster canonical-source rule.** The phase's `profile.json` declares which roles exist and each role's allowed count (`min` / `recommended` / `max`); it names no provider. The user selects the providers and models for each role when the run starts. `resultContract.workerRoles` in `task-manifest.json` is the roster materialized for this run, and it is the only roster the lead dispatches against. `resultContract.criticRoles` lists a selected critic.
46
46
 
47
- Only workers selected from `recommendedWorkers` in `task-manifest.json` and `resultContract.requiredWorkerRoles` become required roles.
48
-
49
- - If one worker is selected: "`<role>` is the required worker role for this run."
50
- - If two or more workers are selected: "`<role1>`, `<role2>`, and `<role3>` are required worker roles."
51
- - If Antigravity is selected: "`Antigravity worker` must be attempted in this workflow."
52
- - If Antigravity is not selected: "`Antigravity worker` is not selected for this run, so it does not need to be attempted."
47
+ Every role slot in `resultContract.workerRoles` and `resultContract.criticRoles` is attempted. A run with one analysis worker proceeds without cross-model verification; the final report records that no cross-model check ran.
53
48
 
54
49
  ## Operating Rules
55
50
 
@@ -57,10 +52,10 @@ Only workers selected from `recommendedWorkers` in `task-manifest.json` and `res
57
52
  1. The lead is responsible for orchestration, convergence supervision, and final-report review/approval. It never overrides worker analysis and never bypasses a rostered Report writer worker.
58
53
  2. `Report writer worker` is NOT an analysis worker. It is excluded from Phase 4/5 (initial analysis) and Phase 5.5 (convergence re-verification). It is spawned only in Phase 6 and authors only the narrative input consumed by report assembly.
59
54
  3. When `Report writer worker` is in the roster, Lead MUST dispatch it in Phase 6 as a separate invocation after convergence. Omit it from Phase 4/5 analysis selection and pass `--workers report-writer` for a CLI-backed Phase 6 call. Contract v3 has no lead-authored fallback: an attempted dispatch ending in `error` / `timeout` / `not-run` is retried or leaves the run blocked. **Enforced:** `dispatch_core._validate_report_writer_isolation()` rejects every mixed analysis/report plan before process creation, and the default roster selectors exclude `report-writer`; report-writer write paths exclude the final record.
60
- 4. The assigned model for each role is maintained based on `resultContract.requiredWorkerRoles` in task-manifest.json and the lead model metadata.
61
- 5. Required roles must not be replaced by unnamed generic parallel workers.
62
- 6. Before dispatching any required worker, persist the exact worker prompt to the assigned current-run prompt history path under `runs/<task-type>/prompts/`.
63
- 7. Before the final decision, collect results or explicit terminal statuses for each required worker role.
55
+ 4. The assigned model for each role is maintained based on `resultContract.workerRoles` in task-manifest.json and the lead model metadata.
56
+ 5. Selected roles must not be replaced by unnamed generic parallel workers.
57
+ 6. Before dispatching any selected worker, persist the exact worker prompt to the assigned current-run prompt history path under `runs/<task-type>/prompts/`.
58
+ 7. Before the final decision, collect results or explicit terminal statuses for each selected worker role.
64
59
  8. If a worker is attempted with status `completed`, `timeout`, or `error`, the corresponding worker prompt history file must actually exist.
65
60
  9. If a worker result is `completed`, the corresponding worker result file must actually exist.
66
61
  10. Treat `team-state` as the canonical source; if it differs from the report's role label, follow `team-state`.
@@ -90,7 +85,7 @@ Inject only the packet-scoped one-line pointer into every analysis worker's prom
90
85
 
91
86
  Persist the exact worker prompt before dispatch per Operating Rule 6; never use `/tmp/*prompt*.txt` as the canonical artifact path.
92
87
 
93
- Send byte-identical dispatch prompts to every analysis worker per the "Dispatch-prompt invariant" (Role Definitions). Specialization lives in Section 6 of the worker output, not in the dispatch prompt body.
88
+ Send byte-identical dispatch prompts to every analysis worker per the "Dispatch-prompt invariant" (Role Definitions). Specialization lives in Section 7 of the worker output, not in the dispatch prompt body.
94
89
 
95
90
  ### Audience preambles + shared error contract (SSOT)
96
91
 
@@ -203,7 +198,7 @@ The same audit check parses command evidence from canonical rows such as `- Evid
203
198
 
204
199
  ## Worker Output Contract
205
200
 
206
- The canonical analysis-worker output contract — frontmatter, sections 1–5 plus optional Section 6, item IDs, and ticket tagging — lives in `templates/worker-prompt-preamble.md`. Implementation output behavior remains in its executor/verifier sidecars; report authoring remains in the report-writer preamble and Phase 6 contract.
201
+ The canonical analysis-worker output contract — frontmatter, sections 1–5, Section 6 Coverage Verdicts when the packet carries a census, optional Section 7, item IDs, and ticket tagging — lives in `templates/worker-prompt-preamble.md`. Implementation output behavior remains in its executor/verifier sidecars; report authoring remains in the report-writer preamble and Phase 6 contract.
207
202
 
208
203
  Lead-facing duties that stay here:
209
204
 
@@ -5,9 +5,9 @@ Edit here once; every profile picks the change up at next render. Do NOT
5
5
  add phase-specific rules to this file — phase rules stay in the per-
6
6
  profile document.
7
7
  -->
8
- - Team contract (shared): roster roles, model-assignment rules, dispatch invariants, and required-worker attempt rules are canonical in the team contract (`prompts/lead/team-contract.md`). Two consequences every phase honours: the host-native Okstra lead is synthesis-only (in `implementation`, distinct from the `Executor` and verifiers), and unnamed generic parallel workers never replace or extend the per-profile `Required workers:` roster. Prep-time model recommendations come from the catalog defaults in `okstra_ctl.models` (for example, `Codex worker` → `gpt-6.1-sol`); at dispatch time the task-manifest's materialized assignment is the only source — there is no dispatch-time fallback.
8
+ - Team contract (shared): roster roles, model-assignment rules, dispatch invariants, and worker attempt rules are canonical in the team contract (`prompts/lead/team-contract.md`). Two consequences every phase honours: the host-native Okstra lead is synthesis-only (in `implementation`, distinct from the `Executor` and verifiers), and unnamed generic parallel workers never replace or extend the run's selected roster (`resultContract.workerRoles`). Prep-time model recommendations come from the catalog defaults in `okstra_ctl.models` (for example, `Codex worker` → `gpt-6.1-sol`); at dispatch time the task-manifest's materialized assignment is the only source — there is no dispatch-time fallback.
9
9
  - Worker interaction model (shared — read before inferring behaviour from the roster):
10
- - the per-profile `Required workers:` block is a **roster**, not a behaviour contract. Each role's interaction mode changes across operating phases of the same run.
10
+ - the run's selected roster is a **roster**, not a behaviour contract. Each role's interaction mode changes across operating phases of the same run.
11
11
  - **Phase 4 / 5 (independent analysis)**: every analyser in the resolved provider assignment roster produces findings independently and has no access to another worker's output. `report-writer` does not analyse.
12
12
  - **Phase 5.5 (convergence — peer review by workers)**: workers peer-review each other's findings across up to `effectiveMaxRounds` rounds; the lead mediates but does not vote. See `prompts/lead/convergence.md` for the round protocol (replay of findings, `AGREE` / `DISAGREE` / `SUPPLEMENT` verdicts), queue invariants, and final classification (`full-consensus` / `partial-consensus` / `contested` / `worker-unique` / `unverified`). For `requirements-discovery`, `error-analysis`, `implementation-option-selection`, `implementation-planning`, `project-analysis`, `feature-analysis`, and `change-impact-analysis` this phase runs in **adversarial mode** (`convergence.adversarial=true`): verifiers try to refute each finding against its cited evidence and the burden of proof sits on the claim — see that skill's §"Adversarial Verification Mode".
13
13
  - Do NOT conclude "no peer review happens" from the roster alone — every profile that lists ≥2 analyser workers runs convergence by default (`convergence.enabled=true` in `task-manifest.json`).
@@ -95,7 +95,7 @@ profile document.
95
95
  - Verdict Card data consistency (shared; schema-v1 Markdown keeps the legacy visible card):
96
96
  - The Card carries no verdict token — the token lives once, in `finalVerdict.verdictToken`, and every gate reads it there. `verdictCard.direction` byte-matches `finalVerdict.direction`; next-step routing agrees with `recommendedNextSteps[0]`. The full reading copy and human summary are derived from the data fields without repeating both visible sections. **Enforced in part:** the v3.0 schema's `verdictCard` is `additionalProperties: false` with no verdict-token property, so the token cannot be duplicated onto the Card, and `scripts/okstra_ctl/report_narrative.py` `writer_owned_schema` applies the finished report's `$defs.Direction` enum to the narrative, rejecting an off-enum `direction` while the writer can still be re-run. The byte-match between the two `direction` fields is not compared by anything — assembly overwrites `nextStep` on both when the plan-body gate passes (`scripts/okstra_ctl/report_assembly.py:590-604`) but leaves `direction` as the writer wrote it.
97
97
  - Cross-worker traceability (shared — applies to every analysis worker output and to the lead's `## 6.` / `## 2.` tables in the final-report):
98
- - **Worker-side item IDs (free-form but unique within the worker).** Every row item in sections 1–5 (and any optional section 6) of an analysis worker's output MUST carry an item ID that is unique within that one worker's result file. The ID convention is the worker's choice — `F-001` / `F-002` per the suggested schema, `1.1` / `1.2` / `1.3` as Codex tends to use, or any other shape — but it MUST appear as the leading column of the row (for table-form items) or as a `[<ID>]` prefix (for bullet/numbered items). Workers that emit findings without IDs make cross-worker reconciliation impossible.
98
+ - **Worker-side item IDs (free-form but unique within the worker).** Every row item in sections 1–5 (and any optional section 7) of an analysis worker's output MUST carry an item ID that is unique within that one worker's result file. The ID convention is the worker's choice — `F-001` / `F-002` per the suggested schema, `1.1` / `1.2` / `1.3` as Codex tends to use, or any other shape — but it MUST appear as the leading column of the row (for table-form items) or as a `[<ID>]` prefix (for bullet/numbered items). Workers that emit findings without IDs make cross-worker reconciliation impossible.
99
99
  - **Lead-side ID assignment + source preservation.** When the lead (or `report-writer-worker`) synthesises consensus, difference, or primary-evidence rows from worker outputs, the lead assigns a fresh `C-NNN` / `D-NNN` / `E-NNN` row ID. Each `sourceItems` field MUST list every contributing worker:item pair (e.g. `claude:F-001`, `codex:1.1`, `grok:F-3`, `kimi:2.4`) so an agent can trace the synthesised row to the worker result. Bare worker names are rejected. **Enforced:** `schemas/final-report-v2.0.schema.json` `$defs.SourceItem` pins each entry to `^[a-z][a-z-]*:[A-Za-z0-9._-]+$`, and `ConsensusRow` / `PrimaryEvidenceRow` require non-empty `sourceItems`.
100
100
  - Audit sidecar (shared): Reading Confirmation placement follows the audience-selected preamble named by `**Worker Preamble Path:**`. Profiles do not restate it; the main worker-results body starts at section 1.
101
101
 
@@ -473,30 +473,6 @@
473
473
  "project_default": "pr-template: {value} (project default)"
474
474
  }
475
475
  },
476
- "executor": {
477
- "label": "실행자 (executor)?",
478
- "echo_template": "executor: {value}",
479
- "options": {
480
- "_DEFAULT_SUFFIX": " (default)"
481
- }
482
- },
483
- "critic_pick": {
484
- "label": "critic 모델을 고르세요 (매 런 1명. 동수 계획 항목을 이 역할이 가릅니다)",
485
- "echo_template": "critic: {value}",
486
- "options": {},
487
- "labels": {
488
- "provider_recommended": "{provider} critic (추천)",
489
- "provider": "{provider} critic"
490
- }
491
- },
492
- "critic_text": {
493
- "label": "critic provider 를 선택하세요",
494
- "echo_template": "critic: {value}",
495
- "labels": {
496
- "provider_recommended": "{provider} critic (추천)",
497
- "provider": "{provider} critic"
498
- }
499
- },
500
476
  "reuse_previous": {
501
477
  "label": "이 task·phase 의 직전 run 설정이 남아 있습니다. 그대로 재사용할까요?\n· 예 — 직전 run 의 워커 구성·역할별 모델·directive·관련 task 를 그대로 가져오고, 가장 최근 final-report 를 clarification 입력으로 자동 선택해 바로 확인 단계로 넘어갑니다.\n· 아니오 — 모델/워커/directive 등을 단계별로 다시 입력합니다.",
502
478
  "echo_template": "reuse-previous: {value}",
@@ -513,72 +489,6 @@
513
489
  "default_suffix": " (기본 후보)"
514
490
  }
515
491
  },
516
- "defaults_or_custom": {
517
- "label": "역할별 모델 선택 단계입니다 (참여 워커 구성을 바꾸는 게 아닙니다).\n이번 run 에서 모델을 고를 역할:\n{role_models}\n· 기본값으로 진행 — 위 추천 모델을 그대로 쓰고, directive·관련 task 없이 바로 넘어갑니다.\n· 커스터마이즈 — 위 역할별 모델을 직접 고르고, 추가 directive·관련 task 도 지정합니다.\n(추천 값은 runtime 기본값으로 해소됩니다. 분석에 참여하지 않는 역할은 위 목록에 나오지 않습니다 — 예: antigravity 는 implementation 의 executor 로 선택했을 때만 표시됩니다.)",
518
- "echo_template": "customize: {value}",
519
- "options": {
520
- "defaults": "기본값으로 진행 (역할별 추천 모델 그대로)",
521
- "customize": "커스터마이즈 (역할별 모델 직접 선택)"
522
- }
523
- },
524
- "workers_override": {
525
- "label": "이번 run 에 추가할 분석 워커를 선택해주세요 (최소 1개, 여러 개 가능).\n기본 워커와 report-writer 는 항상 참여하므로 목록에 없습니다 — 기본 워커에서 일부를 빼려면 '직접 선택'을 고르세요.",
526
- "echo_template": "workers: {value}",
527
- "labels": {
528
- "default_roster": "기본 워커만 ({workers}) — 옵션 워커 추가 없음",
529
- "add_optional": "{worker} 추가 (옵션)"
530
- },
531
- "options": {
532
- "__free_input__": "직접 선택 (기본 워커까지 포함한 전체 목록에서 고르기)"
533
- },
534
- "errors": {
535
- "min_one_required": "워커를 최소 1개 선택해주세요",
536
- "custom_must_be_alone": "'직접 선택'은 다른 항목과 함께 고를 수 없습니다 — 단독으로 선택해주세요",
537
- "unknown_option": "목록에 없는 항목입니다: {values}"
538
- }
539
- },
540
- "workers_custom": {
541
- "label": "참여시킬 분석 워커를 직접 선택해주세요 (최소 1개). report-writer 는 항상 포함됩니다.",
542
- "echo_template": "workers: {value}",
543
- "options": {
544
- "_OPTIONAL_SUFFIX": " (옵션)"
545
- },
546
- "errors": {
547
- "min_one_required": "워커를 최소 1개 선택해주세요"
548
- }
549
- },
550
- "lead_model": {
551
- "label": "리더(Okstra lead) 제공자와 모델?",
552
- "echo_template": "lead-model: {value}"
553
- },
554
- "executor_model": {
555
- "label": "실행자({executor}) 모델?",
556
- "echo_template": "{executor}-model: {value}"
557
- },
558
- "claude_model": {
559
- "label": "claude 워커 모델?",
560
- "echo_template": "claude-model: {value}"
561
- },
562
- "codex_model": {
563
- "label": "codex 워커 모델?",
564
- "echo_template": "codex-model: {value}"
565
- },
566
- "antigravity_model": {
567
- "label": "antigravity 워커 모델?",
568
- "echo_template": "antigravity-model: {value}"
569
- },
570
- "grok_model": {
571
- "label": "Grok 워커 모델?",
572
- "echo_template": "grok-model: {value}"
573
- },
574
- "kimi_model": {
575
- "label": "Kimi 워커 모델?",
576
- "echo_template": "kimi-model: {value}"
577
- },
578
- "report_writer_model": {
579
- "label": "리포트 작성자(report-writer) 모델?",
580
- "echo_template": "report-writer-model: {value}"
581
- },
582
492
  "directive": {
583
493
  "label": "추가 directive 가 있으면 적어주세요 (없으면 빈 줄)",
584
494
  "echo_template": "directive: {value}"
@@ -648,7 +558,6 @@
648
558
  "provider_data_scope": "\n전달 대상: 위 역할·모델 목록의 제공자(현재 세션 및 선택한 외부 모델 제공자).\n전달 자료: `{project_root}`의 이 작업에 대한 작업 개요, 작업 수행에 필요한 저장소 소스·문서, 선택한 근거 자료 및 실행 중 생성되는 관련 분석·검증 결과.\n진행을 선택하면 위 대상에 해당 자료를 전달하여 선택한 작업을 실행하는 것을 승인합니다. 선택에 없는 제공자나 작업과 무관한 자료는 승인 범위에 포함되지 않습니다. 실행 환경의 권한 검토는 별도로 적용됩니다.",
649
559
  "static_role": " static-role : {role}#{ordinal} / {model}",
650
560
  "dynamic_role": " dynamic-role : {role} / reuse selected participant model",
651
- "workers_implementation_default": " workers : (프로필 기본 — executor + verifier 2 + report-writer)",
652
561
  "base_ref_stage_isolated": " base-ref : (stage 격리 — 의존 stage 기준으로 run 준비 시점에 자동 해소)",
653
562
  "base_ref_reuse_task_dir": " base-ref : (기존 `{task_key}` 디렉터리 재사용 — 최초 base 유지)",
654
563
  "worktree_new": " worktree : 새 브랜치 `{branch}` (base-ref `{base_ref}`) → `{path}`",
@@ -57,6 +57,12 @@ CLAUDE = {
57
57
  "sonnet-5", "sonnet-5", "claude-sonnet-5", aliases=("claude-sonnet-5",),
58
58
  channel_family="sonnet", selectable=False,
59
59
  ),
60
+ # sonnet 채널이 지금 서빙하는 점-릴리스(opus-5-5 와 같은 이유). 실측
61
+ # 2026-10-04 dev-11118 implementation-planning: report-writer 4회 전부 exit 78.
62
+ "sonnet-5-5": ModelSpec(
63
+ "sonnet-5-5", "sonnet-5-5", "claude-sonnet-5-5", aliases=("claude-sonnet-5-5",),
64
+ channel_family="sonnet", selectable=False,
65
+ ),
60
66
  "haiku": ModelSpec(
61
67
  "haiku", "haiku", "haiku", version_kind="channel", channel_family="haiku"
62
68
  ),
@@ -41,6 +41,7 @@ AgentAudience = Literal[
41
41
  "translator",
42
42
  "code-reviewer",
43
43
  "schedule-verifier",
44
+ "business-flow-investigator",
44
45
  ]
45
46
 
46
47
  _SUPPORTED_AUDIENCES = frozenset(get_args(AgentAudience))
@@ -2535,4 +2536,3 @@ def _load_role_duty(path: Path) -> DutyContract:
2535
2536
  source_path=path,
2536
2537
  )
2537
2538
 
2538
-
@@ -178,6 +178,7 @@ def require_invocation_flags(args: argparse.Namespace, label: str) -> None:
178
178
  missing = [
179
179
  "--" + flag for flag in _REQUIRED_FLAGS
180
180
  if not getattr(args, flag.replace("-", "_"))
181
+ and not (flag == "instruction" and getattr(args, "corrections", None))
181
182
  ]
182
183
  if missing:
183
184
  raise AgentPromptCliError(f"{label} requires: " + ", ".join(missing))