okstra 0.140.0 → 0.141.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 (37) hide show
  1. package/README.md +1 -1
  2. package/docs/architecture.md +1 -1
  3. package/docs/cli.md +1 -1
  4. package/package.json +1 -1
  5. package/runtime/BUILD.json +2 -2
  6. package/runtime/agents/workers/report-writer-worker.md +4 -2
  7. package/runtime/prompts/launch.template.md +7 -5
  8. package/runtime/prompts/lead/convergence.md +22 -0
  9. package/runtime/prompts/lead/plan-body-verification.md +45 -6
  10. package/runtime/prompts/lead/report-writer.md +2 -0
  11. package/runtime/prompts/profiles/error-analysis.md +1 -0
  12. package/runtime/prompts/profiles/final-verification.md +1 -1
  13. package/runtime/prompts/profiles/implementation-planning.md +6 -4
  14. package/runtime/prompts/profiles/improvement-discovery.md +2 -1
  15. package/runtime/prompts/profiles/requirements-discovery.md +2 -1
  16. package/runtime/python/okstra_ctl/build_tools.py +95 -0
  17. package/runtime/python/okstra_ctl/incremental_scope.py +95 -0
  18. package/runtime/python/okstra_ctl/render.py +26 -11
  19. package/runtime/python/okstra_ctl/render_final_report.py +24 -1
  20. package/runtime/python/okstra_ctl/rollup.py +49 -0
  21. package/runtime/python/okstra_ctl/scope_provenance.py +98 -1
  22. package/runtime/python/okstra_ctl/stage_citations.py +47 -0
  23. package/runtime/python/okstra_ctl/workflow.py +7 -4
  24. package/runtime/schemas/final-report-v1.0.schema.json +60 -1
  25. package/runtime/skills/okstra-brief-gen/SKILL.md +38 -11
  26. package/runtime/skills/okstra-rollup/SKILL.md +3 -2
  27. package/runtime/templates/reports/brief.template.md +42 -11
  28. package/runtime/templates/reports/fan-out-unit.template.md +9 -4
  29. package/runtime/templates/reports/final-report.template.md +20 -0
  30. package/runtime/templates/reports/final-verification-input.template.md +4 -2
  31. package/runtime/templates/reports/i18n/en.json +11 -4
  32. package/runtime/templates/reports/i18n/ko.json +11 -4
  33. package/runtime/templates/reports/improvement-discovery-input.template.md +2 -2
  34. package/runtime/validators/validate-brief.py +171 -26
  35. package/runtime/validators/validate-run.py +450 -78
  36. package/runtime/validators/validate_fanout.py +19 -10
  37. package/runtime/validators/validate_improvement_report.py +17 -4
@@ -181,10 +181,13 @@ def _check_candidate_row(
181
181
  errors: list[str],
182
182
  ) -> None:
183
183
  """Items 2, 3, 4, 6, 7 — validate a single candidate row."""
184
- if len(row) != 10:
185
- errors.append(f"row {idx} has {len(row)} columns, expected 10")
184
+ if len(row) != 11:
185
+ errors.append(f"row {idx} has {len(row)} columns, expected 11")
186
186
  return
187
- cand_id, lens_cell, _title, scope, _sev, _eff, consensus, source_workers, next_phase, _evidence = row
187
+ (
188
+ cand_id, lens_cell, _title, scope, _sev, _eff,
189
+ consensus, source_workers, next_phase, expected_after, _evidence,
190
+ ) = row
188
191
 
189
192
  if not _CAND_ID_RE.match(cand_id):
190
193
  errors.append(f"row {idx}: Cand ID '{cand_id}' must match I-NNN")
@@ -209,6 +212,15 @@ def _check_candidate_row(
209
212
  if next_phase not in _NEXT_PHASES:
210
213
  errors.append(f"row {idx}: Recommended next-phase '{next_phase}' must be one of {_NEXT_PHASES}")
211
214
 
215
+ # A candidate nobody can describe an observable change for is not a finding;
216
+ # it is a preference. Making this a cell rather than prose is what stops the
217
+ # candidate list from growing past what a downstream brief can pin.
218
+ if not expected_after.strip():
219
+ errors.append(
220
+ f"row {idx}: `Expected behavior after` is empty — state what becomes "
221
+ "observably different once this candidate is applied, or drop the row"
222
+ )
223
+
212
224
 
213
225
  def _check_candidate_rows(
214
226
  data_rows: list[list[str]],
@@ -233,7 +245,8 @@ def _check_candidates_table(
233
245
  header, *data = rows
234
246
  expected_columns = [
235
247
  "Cand ID", "Lens", "Title", "Scope", "Severity", "Effort",
236
- "Consensus", "Source workers", "Recommended next-phase", "Evidence",
248
+ "Consensus", "Source workers", "Recommended next-phase",
249
+ "Expected behavior after", "Evidence",
237
250
  ]
238
251
  if header != expected_columns:
239
252
  errors.append(