okstra 0.172.0 → 0.174.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 (123) hide show
  1. package/README.md +8 -6
  2. package/docs/architecture/storage-model.md +24 -3
  3. package/docs/architecture.md +21 -35
  4. package/docs/cli.md +39 -7
  5. package/docs/container.md +1 -1
  6. package/docs/contributor-change-matrix.md +1 -1
  7. package/docs/performance-improvement-plan-v2.md +6 -5
  8. package/docs/project-structure-overview.md +33 -25
  9. package/docs/task-process/README.md +6 -4
  10. package/docs/task-process/error-analysis.md +2 -2
  11. package/docs/task-process/final-verification.md +2 -2
  12. package/docs/task-process/implementation-option-selection.md +70 -0
  13. package/docs/task-process/implementation-planning.md +24 -16
  14. package/docs/task-process/requirements-discovery.md +2 -2
  15. package/package.json +1 -1
  16. package/runtime/BUILD.json +2 -2
  17. package/runtime/agents/workers/claude-worker.md +1 -1
  18. package/runtime/agents/workers/report-writer-worker.md +30 -6
  19. package/runtime/bin/lib/okstra/cli.sh +5 -1
  20. package/runtime/bin/lib/okstra/globals.sh +2 -1
  21. package/runtime/bin/lib/okstra/usage.sh +3 -0
  22. package/runtime/bin/okstra-provider-exec.py +29 -12
  23. package/runtime/bin/okstra-trace-cleanup.sh +58 -129
  24. package/runtime/bin/okstra.sh +2 -0
  25. package/runtime/prompts/duties/direction-selection-worker.md +44 -0
  26. package/runtime/prompts/duties/planning-worker.md +12 -4
  27. package/runtime/prompts/lead/adapters/cmux.md +2 -0
  28. package/runtime/prompts/lead/context-loader.md +1 -1
  29. package/runtime/prompts/lead/convergence.md +5 -5
  30. package/runtime/prompts/lead/okstra-lead-contract.md +7 -6
  31. package/runtime/prompts/lead/plan-body-verification.md +23 -6
  32. package/runtime/prompts/lead/report-writer.md +33 -11
  33. package/runtime/prompts/profiles/_common-contract.md +3 -3
  34. package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
  35. package/runtime/prompts/profiles/_implementation-executor.md +2 -0
  36. package/runtime/prompts/profiles/_implementation-verifier.md +2 -2
  37. package/runtime/prompts/profiles/error-analysis.md +4 -4
  38. package/runtime/prompts/profiles/final-verification.md +3 -3
  39. package/runtime/prompts/profiles/forbidden-actions.json +7 -0
  40. package/runtime/prompts/profiles/implementation-option-selection.md +35 -0
  41. package/runtime/prompts/profiles/implementation-planning.md +61 -46
  42. package/runtime/prompts/profiles/implementation.md +4 -2
  43. package/runtime/prompts/profiles/improvement-discovery.md +1 -1
  44. package/runtime/prompts/profiles/release-handoff.md +1 -1
  45. package/runtime/prompts/profiles/requirements-discovery.md +3 -3
  46. package/runtime/prompts/wizard/prompts.ko.json +9 -1
  47. package/runtime/python/okstra_ctl/adapters/dispatch/__init__.py +1 -6
  48. package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +4 -4
  49. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +5 -0
  50. package/runtime/python/okstra_ctl/agent_invocation.py +1 -0
  51. package/runtime/python/okstra_ctl/analysis_packet.py +6 -0
  52. package/runtime/python/okstra_ctl/conformance.py +68 -0
  53. package/runtime/python/okstra_ctl/dispatch_core.py +89 -39
  54. package/runtime/python/okstra_ctl/dispatch_state.py +142 -14
  55. package/runtime/python/okstra_ctl/doctor.py +2 -2
  56. package/runtime/python/okstra_ctl/domain/worker_exec.py +5 -0
  57. package/runtime/python/okstra_ctl/exact_coverage.py +128 -0
  58. package/runtime/python/okstra_ctl/final_report_schema.py +5 -4
  59. package/runtime/python/okstra_ctl/fix_cycles.py +3 -1
  60. package/runtime/python/okstra_ctl/implementation_direction.py +836 -0
  61. package/runtime/python/okstra_ctl/implementation_options.py +479 -0
  62. package/runtime/python/okstra_ctl/pane_reclaim.py +13 -22
  63. package/runtime/python/okstra_ctl/plan_items.py +51 -3
  64. package/runtime/python/okstra_ctl/render.py +1 -0
  65. package/runtime/python/okstra_ctl/render_final_report.py +16 -19
  66. package/runtime/python/okstra_ctl/report_contract.py +45 -14
  67. package/runtime/python/okstra_ctl/report_finalize.py +68 -9
  68. package/runtime/python/okstra_ctl/report_html/render.py +4 -2
  69. package/runtime/python/okstra_ctl/report_html/router.py +4 -0
  70. package/runtime/python/okstra_ctl/report_html/view_models/implementation_option_selection.py +32 -0
  71. package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +25 -10
  72. package/runtime/python/okstra_ctl/report_views.py +148 -12
  73. package/runtime/python/okstra_ctl/run.py +393 -4
  74. package/runtime/python/okstra_ctl/schema_excerpt.py +1 -1
  75. package/runtime/python/okstra_ctl/scope_provenance.py +16 -10
  76. package/runtime/python/okstra_ctl/session.py +69 -12
  77. package/runtime/python/okstra_ctl/team.py +51 -25
  78. package/runtime/python/okstra_ctl/tmux.py +19 -149
  79. package/runtime/python/okstra_ctl/user_response.py +75 -0
  80. package/runtime/python/okstra_ctl/wizard.py +144 -0
  81. package/runtime/python/okstra_ctl/worker_prompt_policy.py +2 -0
  82. package/runtime/python/okstra_ctl/worker_request.py +2 -0
  83. package/runtime/python/okstra_ctl/workflow.py +29 -7
  84. package/runtime/python/okstra_ctl/worktree.py +69 -3
  85. package/runtime/python/okstra_token_usage/cli.py +1 -1
  86. package/runtime/python/okstra_token_usage/collect.py +66 -6
  87. package/runtime/schemas/final-report-v2.0.schema.json +1428 -137
  88. package/runtime/skills/okstra-setup/references/project-config.md +11 -0
  89. package/runtime/templates/reports/final-report-v2.template.md +4 -0
  90. package/runtime/templates/reports/final-verification-input.template.md +1 -1
  91. package/runtime/templates/reports/html/base.template.html +3 -2
  92. package/runtime/templates/reports/html/i18n/en.json +21 -1
  93. package/runtime/templates/reports/html/i18n/ko.json +21 -1
  94. package/runtime/templates/reports/html/macros/forms.html +21 -2
  95. package/runtime/templates/reports/html/tasks/implementation-option-selection.template.html +49 -0
  96. package/runtime/templates/reports/html/tasks/implementation-planning.template.html +36 -2
  97. package/runtime/templates/reports/i18n/en.json +13 -0
  98. package/runtime/templates/reports/implementation-input.template.md +4 -2
  99. package/runtime/templates/reports/implementation-planning-input.template.md +18 -4
  100. package/runtime/templates/reports/improvement-discovery-input.template.md +1 -1
  101. package/runtime/templates/reports/md/tasks/implementation-option-selection.template.md +13 -0
  102. package/runtime/templates/reports/md/tasks/implementation-planning.template.md +17 -0
  103. package/runtime/templates/reports/report.js +111 -4
  104. package/runtime/templates/reports/settings.template.json +0 -24
  105. package/runtime/templates/reports/task-brief.template.md +9 -3
  106. package/runtime/templates/reports/user-response.template.md +25 -4
  107. package/runtime/templates/worker-prompt-preamble.md +8 -0
  108. package/runtime/validators/lib/fixtures.sh +49 -17
  109. package/runtime/validators/validate-implementation-plan-stages.py +169 -4
  110. package/runtime/validators/validate-report-views.py +2 -2
  111. package/runtime/validators/validate-run.py +149 -498
  112. package/runtime/validators/validate_improvement_report.py +5 -1
  113. package/runtime/validators/validate_session_conformance.py +1 -1
  114. package/src/cli-registry.mjs +8 -1
  115. package/src/commands/execute/codex-run.mjs +1 -0
  116. package/src/commands/execute/render-bundle.mjs +1 -0
  117. package/src/commands/execute/team.mjs +3 -3
  118. package/src/commands/execute/worktree-status.mjs +109 -0
  119. package/src/commands/lifecycle/install.mjs +0 -2
  120. package/src/commands/report/finalize.mjs +13 -6
  121. package/runtime/bin/okstra-subagent-reclaim.sh +0 -26
  122. package/runtime/schemas/final-report-v1.0.schema.json +0 -6366
  123. package/runtime/templates/reports/final-report.template.md +0 -1258
@@ -0,0 +1,128 @@
1
+ """Pure exact requirement coverage calculation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping, Sequence
6
+ from dataclasses import dataclass
7
+ from typing import Literal
8
+
9
+
10
+ class ExactCoverageError(ValueError):
11
+ """Raised when coverage input cannot define a valid comparison."""
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ExactCoverageResult:
16
+ coverage_percent: float
17
+ scope_precision_percent: float
18
+ covered_count: int
19
+ total_count: int
20
+ unmapped_commitments: tuple[str, ...]
21
+ contradicted_requirements: tuple[str, ...]
22
+ verdict: Literal["exact", "under", "over", "contradicted"]
23
+
24
+ def as_report_summary(self) -> dict[str, object]:
25
+ return {
26
+ "coveragePercent": self.coverage_percent,
27
+ "scopePrecisionPercent": self.scope_precision_percent,
28
+ "coveredCount": self.covered_count,
29
+ "totalCount": self.total_count,
30
+ "unmappedCommitments": list(self.unmapped_commitments),
31
+ "contradictedRequirements": list(self.contradicted_requirements),
32
+ "coverageVerdict": self.verdict,
33
+ }
34
+
35
+
36
+ def calculate_exact_coverage(
37
+ original_requirement_ids: Sequence[str],
38
+ coverage_status_by_id: Mapping[str, str],
39
+ commitment_requirement_ids: Mapping[str, Sequence[str]],
40
+ ) -> ExactCoverageResult:
41
+ original_ids = tuple(original_requirement_ids)
42
+ if not original_ids:
43
+ raise ExactCoverageError("original requirement denominator is empty")
44
+ if len(set(original_ids)) != len(original_ids):
45
+ raise ExactCoverageError("original requirement ids contain duplicates")
46
+
47
+ original_id_set = set(original_ids)
48
+ if set(coverage_status_by_id) != original_id_set:
49
+ raise ExactCoverageError("coverage rows must match original requirement ids")
50
+ allowed_statuses = {"covered", "not-covered", "contradicted"}
51
+ if any(status not in allowed_statuses for status in coverage_status_by_id.values()):
52
+ raise ExactCoverageError("coverage status is invalid")
53
+ if not commitment_requirement_ids:
54
+ raise ExactCoverageError("implementation commitment denominator is empty")
55
+
56
+ unmapped = tuple(
57
+ commitment_id
58
+ for commitment_id, requirement_ids in commitment_requirement_ids.items()
59
+ if not requirement_ids
60
+ or any(requirement_id not in original_id_set for requirement_id in requirement_ids)
61
+ )
62
+ contradicted = tuple(
63
+ requirement_id
64
+ for requirement_id in original_ids
65
+ if coverage_status_by_id[requirement_id] == "contradicted"
66
+ )
67
+ covered_count = sum(
68
+ coverage_status_by_id[requirement_id] == "covered"
69
+ for requirement_id in original_ids
70
+ )
71
+ coverage_percent = round(covered_count / len(original_ids) * 100, 2)
72
+ traceable_count = len(commitment_requirement_ids) - len(unmapped)
73
+ scope_precision_percent = round(
74
+ traceable_count / len(commitment_requirement_ids) * 100,
75
+ 2,
76
+ )
77
+ verdict: Literal["exact", "under", "over", "contradicted"]
78
+ if contradicted:
79
+ verdict = "contradicted"
80
+ elif covered_count != len(original_ids):
81
+ verdict = "under"
82
+ elif unmapped:
83
+ verdict = "over"
84
+ else:
85
+ verdict = "exact"
86
+
87
+ return ExactCoverageResult(
88
+ coverage_percent=coverage_percent,
89
+ scope_precision_percent=scope_precision_percent,
90
+ covered_count=covered_count,
91
+ total_count=len(original_ids),
92
+ unmapped_commitments=unmapped,
93
+ contradicted_requirements=contradicted,
94
+ verdict=verdict,
95
+ )
96
+
97
+
98
+ def calculate_plan_exact_coverage(
99
+ original_requirement_ids: Sequence[str],
100
+ coverage_status_by_id: Mapping[str, str],
101
+ stage_requirement_ids: Mapping[int, Sequence[str]],
102
+ file_requirement_ids: Mapping[str, Sequence[str]],
103
+ ) -> tuple[ExactCoverageResult, tuple[int, ...], tuple[str, ...]]:
104
+ """Apply exact coverage to the executable stage and file-change scope."""
105
+ commitments = {
106
+ **{
107
+ f"stage:{stage}": requirement_ids
108
+ for stage, requirement_ids in stage_requirement_ids.items()
109
+ },
110
+ **{
111
+ f"file:{path}": requirement_ids
112
+ for path, requirement_ids in file_requirement_ids.items()
113
+ },
114
+ }
115
+ result = calculate_exact_coverage(
116
+ original_requirement_ids,
117
+ coverage_status_by_id,
118
+ commitments,
119
+ )
120
+ unmapped_stages = tuple(
121
+ stage for stage, requirement_ids in stage_requirement_ids.items()
122
+ if not requirement_ids
123
+ )
124
+ unmapped_files = tuple(
125
+ path for path, requirement_ids in file_requirement_ids.items()
126
+ if not requirement_ids
127
+ )
128
+ return result, unmapped_stages, unmapped_files
@@ -1,7 +1,7 @@
1
1
  """Mini JSON Schema validator for the final-report data.json.
2
2
 
3
3
  This is a deliberately narrow JSON Schema implementation that supports
4
- exactly the keywords used by ``schemas/final-report-v1.0.schema.json``:
4
+ exactly the keywords used by ``schemas/final-report-v2.0.schema.json``:
5
5
 
6
6
  type, required, properties, additionalProperties, enum, const, pattern,
7
7
  minLength, minItems, maxItems, uniqueItems, items, minimum, maximum, $ref ($defs),
@@ -23,6 +23,8 @@ import re
23
23
  from pathlib import Path
24
24
  from typing import Any
25
25
 
26
+ from okstra_ctl.report_contract import CURRENT_REPORT_SCHEMA_VERSION
27
+
26
28
 
27
29
  class SchemaError(ValueError):
28
30
  """Raised when a schema itself is malformed (e.g. a $ref points
@@ -260,7 +262,6 @@ def validate(data: Any, schema: dict) -> list[str]:
260
262
 
261
263
 
262
264
  SCHEMA_FILENAMES = {
263
- "1.0": "final-report-v1.0.schema.json",
264
265
  "2.0": "final-report-v2.0.schema.json",
265
266
  }
266
267
 
@@ -295,9 +296,9 @@ def load_schema_for_data(data: dict, start: Path | None = None) -> dict:
295
296
 
296
297
  def load_schema(schema_path: Path | None = None) -> dict:
297
298
  """Load the final-report schema. If ``schema_path`` is None, locate
298
- ``schemas/final-report-v1.0.schema.json`` relative to this file's
299
+ ``schemas/final-report-v2.0.schema.json`` relative to this file's
299
300
  repo root.
300
301
  """
301
302
  if schema_path is not None:
302
303
  return json.loads(Path(schema_path).read_text(encoding="utf-8"))
303
- return load_schema_version("1.0")
304
+ return load_schema_version(CURRENT_REPORT_SCHEMA_VERSION)
@@ -27,7 +27,9 @@ _LOCK_FILENAME = ".fix-cycles.lock"
27
27
  # 완료(release-handoff) task 에 fix-cycle 로 재진입할 수 있는 entry phase 들.
28
28
  # prepare(run.py) 의 게이트와 wizard 의 감지 술어가 공유하는 SSOT.
29
29
  FIX_CYCLE_ENTRY_PHASES = (
30
- "requirements-discovery", "error-analysis", "implementation-planning")
30
+ "requirements-discovery", "error-analysis", "implementation-option-selection",
31
+ "implementation-planning",
32
+ )
31
33
 
32
34
 
33
35
  def fix_cycles_path(task_root: Path) -> Path: