okstra 0.189.3 → 0.190.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli-registry.mjs +9 -0
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/chat/chat.mjs +54 -4
- package/dist/commands/chat/chat.mjs.map +1 -1
- package/dist/commands/lifecycle/install.mjs +12 -5
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/lifecycle/uninstall.mjs +2 -1
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
- package/dist/lib/host-config.d.mts +5 -3
- package/dist/lib/host-config.mjs +89 -31
- package/dist/lib/host-config.mjs.map +1 -1
- package/docs/architecture/storage-model.md +4 -1
- package/docs/architecture.md +3 -3
- package/docs/cli.md +11 -9
- package/docs/for-ai/skills/okstra-brief-gen.md +1 -1
- package/docs/for-ai/skills/okstra-chat.md +3 -3
- package/docs/for-ai/skills/okstra-inspect.md +11 -1
- package/docs/for-ai/skills/okstra-rollup.md +1 -0
- package/docs/for-ai/skills/okstra-run.md +5 -4
- package/docs/for-ai/skills/okstra-user-response.md +1 -1
- package/docs/project-structure-overview.md +17 -5
- package/docs/task-process/README.md +1 -1
- package/docs/task-process/error-analysis.md +2 -2
- package/docs/task-process/final-verification.md +1 -1
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +8 -6
- package/docs/task-process/requirements-discovery.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +4 -0
- package/runtime/bin/okstra-report-translate.py +10 -4
- package/runtime/prompts/launch.template.md +11 -5
- package/runtime/prompts/lead/adapters/cmux.md +4 -7
- package/runtime/prompts/lead/context-loader.md +2 -0
- package/runtime/prompts/lead/convergence.md +20 -83
- package/runtime/prompts/lead/okstra-lead-contract.md +24 -6
- package/runtime/prompts/lead/plan-body-verification.md +10 -3
- package/runtime/prompts/lead/report-writer.md +15 -5
- package/runtime/prompts/lead/team-contract.md +15 -9
- package/runtime/prompts/profiles/_common-contract.md +2 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -0
- package/runtime/prompts/profiles/_implementation-diff-review.md +4 -0
- package/runtime/prompts/profiles/_implementation-executor.md +9 -5
- package/runtime/prompts/profiles/_implementation-self-check.md +2 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -4
- package/runtime/prompts/profiles/_stage-discipline.md +12 -5
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +1 -1
- package/runtime/prompts/profiles/implementation.md +2 -0
- package/runtime/prompts/profiles/release-handoff.md +4 -3
- package/runtime/prompts/wizard/prompts.ko.json +2 -1
- package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +6 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/antigravity/relay.md +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +9 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +3 -3
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +17 -3
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/external/relay.md +3 -3
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +1 -0
- package/runtime/python/okstra_ctl/adapters/hosts/kimi/relay.md +2 -2
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +3 -3
- package/runtime/python/okstra_ctl/agent/invocation.py +107 -5
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +16 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +3 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/jobs.py +87 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +72 -3
- package/runtime/python/okstra_ctl/analysis_packet.py +47 -6
- package/runtime/python/okstra_ctl/cmux.py +34 -13
- package/runtime/python/okstra_ctl/consumers.py +30 -7
- package/runtime/python/okstra_ctl/convergence.py +110 -21
- package/runtime/python/okstra_ctl/convergence_provenance.py +83 -0
- package/runtime/python/okstra_ctl/dispatch_core.py +127 -5
- package/runtime/python/okstra_ctl/dispatch_state.py +17 -9
- package/runtime/python/okstra_ctl/domain/host.py +5 -0
- package/runtime/python/okstra_ctl/domain/wizard/interaction.py +1 -0
- package/runtime/python/okstra_ctl/error_log_write.py +44 -4
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +84 -7
- package/runtime/python/okstra_ctl/fixed_text.py +25 -7
- package/runtime/python/okstra_ctl/group_context.py +475 -10
- package/runtime/python/okstra_ctl/handoff.py +109 -32
- package/runtime/python/okstra_ctl/handoff_verification.py +80 -0
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +52 -10
- package/runtime/python/okstra_ctl/lead_progress.py +291 -0
- package/runtime/python/okstra_ctl/model_io/renderers.py +60 -1
- package/runtime/python/okstra_ctl/model_io_cli.py +6 -1
- package/runtime/python/okstra_ctl/ports/worker_dispatch.py +4 -0
- package/runtime/python/okstra_ctl/recap.py +179 -17
- package/runtime/python/okstra_ctl/reconcile.py +55 -4
- package/runtime/python/okstra_ctl/render.py +34 -8
- package/runtime/python/okstra_ctl/report_assembly.py +17 -10
- package/runtime/python/okstra_ctl/report_finalize.py +204 -11
- package/runtime/python/okstra_ctl/report_html/common.py +241 -113
- package/runtime/python/okstra_ctl/report_html/context_links.py +121 -0
- package/runtime/python/okstra_ctl/report_html/models.py +11 -5
- package/runtime/python/okstra_ctl/report_html/render.py +69 -17
- package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +8 -1
- package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +8 -1
- package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +11 -1
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +13 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +9 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_option_selection.py +29 -2
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +14 -9
- package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +8 -1
- package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +15 -1
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +7 -1
- package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +9 -1
- package/runtime/python/okstra_ctl/report_translation.py +58 -1
- package/runtime/python/okstra_ctl/run.py +54 -14
- package/runtime/python/okstra_ctl/stage_targets.py +66 -0
- package/runtime/python/okstra_ctl/verdict_blocks.py +39 -12
- package/runtime/python/okstra_ctl/wizard/__init__.py +148 -0
- package/runtime/python/okstra_ctl/wizard/__main__.py +13 -0
- package/runtime/python/okstra_ctl/wizard/cli.py +176 -0
- package/runtime/python/okstra_ctl/wizard/confirmation.py +331 -0
- package/runtime/python/okstra_ctl/wizard/engine.py +363 -0
- package/runtime/python/okstra_ctl/wizard/ids.py +411 -0
- package/runtime/python/okstra_ctl/wizard/prompts.py +202 -0
- package/runtime/python/okstra_ctl/wizard/registry.py +836 -0
- package/runtime/python/okstra_ctl/wizard/render.py +189 -0
- package/runtime/python/okstra_ctl/wizard/roles.py +737 -0
- package/runtime/python/okstra_ctl/wizard/sources.py +703 -0
- package/runtime/python/okstra_ctl/wizard/state.py +689 -0
- package/runtime/python/okstra_ctl/wizard/statefile.py +145 -0
- package/runtime/python/okstra_ctl/wizard/steps_analysis.py +558 -0
- package/runtime/python/okstra_ctl/wizard/steps_identity.py +828 -0
- package/runtime/python/okstra_ctl/wizard/steps_options.py +340 -0
- package/runtime/python/okstra_ctl/wizard/steps_plan.py +959 -0
- package/runtime/python/okstra_ctl/wizard/steps_roles.py +672 -0
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +43 -3
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +14 -0
- package/runtime/python/okstra_ctl/workflow.py +2 -2
- package/runtime/python/okstra_ctl/worktree/__init__.py +2 -0
- package/runtime/python/okstra_ctl/worktree/cleanliness.py +7 -4
- package/runtime/python/okstra_ctl/worktree/git_ops.py +7 -0
- package/runtime/python/okstra_ctl/worktree/naming.py +15 -6
- package/runtime/python/okstra_ctl/worktree/provision.py +2 -1
- package/runtime/python/okstra_ctl/worktree_registry.py +27 -0
- package/runtime/python/okstra_ctl/wrapper_status.py +4 -0
- package/runtime/schemas/final-report-v2.0.schema.json +4 -0
- package/runtime/schemas/final-report-v3.0.schema.json +4 -0
- package/runtime/skills/okstra-brief-gen/SKILL.md +22 -3
- package/runtime/skills/okstra-chat/SKILL.md +8 -6
- package/runtime/skills/okstra-inspect/SKILL.md +2 -2
- package/runtime/skills/okstra-inspect/facets/recap.md +52 -4
- package/runtime/skills/okstra-rollup/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +17 -7
- package/runtime/skills/okstra-schedule-gen/SKILL.md +2 -0
- package/runtime/skills/okstra-user-response/SKILL.md +1 -1
- package/runtime/templates/reports/brief.template.md +15 -7
- package/runtime/templates/reports/final-verification-input.template.md +2 -0
- package/runtime/templates/reports/group-context.template.md +7 -0
- package/runtime/templates/reports/html/base.template.html +13 -3
- package/runtime/templates/reports/html/i18n/en.json +43 -5
- package/runtime/templates/reports/html/i18n/ko.json +43 -5
- package/runtime/templates/reports/html/macros/forms.html +4 -4
- package/runtime/templates/reports/html/tasks/final-verification.template.html +11 -1
- package/runtime/templates/reports/html/tasks/implementation-option-selection.template.html +16 -15
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +5 -5
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +2 -2
- package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +1 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +3 -0
- package/runtime/templates/reports/improvement-discovery-input.template.md +3 -0
- package/runtime/templates/reports/release-handoff-input.template.md +5 -3
- package/runtime/templates/reports/schedule.template.md +13 -4
- package/runtime/templates/reports/task-brief.template.md +1 -1
- package/runtime/templates/reverify-output-contract.md +5 -0
- package/runtime/templates/worker-error-contract.md +2 -0
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-report-views.py +30 -1
- package/runtime/validators/validate-run.py +194 -25
- package/runtime/validators/validate_session_conformance.py +102 -2
- package/runtime/python/okstra_ctl/wizard.py +0 -7168
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Compact initial final-verification prompt contract."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
from .agent.invocation import EXECUTION_DELIVERY_PREFIXES
|
|
5
|
+
|
|
4
6
|
import json
|
|
5
7
|
import re
|
|
6
8
|
from dataclasses import dataclass
|
|
@@ -73,6 +75,7 @@ _REQUIRED_TARGET_PREFIXES = (
|
|
|
73
75
|
"**Verification target digest:**",
|
|
74
76
|
)
|
|
75
77
|
_WORKER_SPECIFIC_PREFIXES = (
|
|
78
|
+
*EXECUTION_DELIVERY_PREFIXES,
|
|
76
79
|
"**Prompt History Path:**",
|
|
77
80
|
"**Result Path:**",
|
|
78
81
|
"**Audit sidecar path:**",
|
|
@@ -221,9 +224,8 @@ def validate_analysis_prompt_set(prompts: Mapping[str, str]) -> list[str]:
|
|
|
221
224
|
return [f"normalized initial analysis prompts differ across workers: {workers}"]
|
|
222
225
|
|
|
223
226
|
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
# 변종별 응답 형식 뒤에 그대로 덧붙인다.
|
|
227
|
+
# 재검증 출력 계약의 정본은 templates/reverify-output-contract.md 이다.
|
|
228
|
+
# 기존 수기 입력의 공백 차이는 허용하면서 세 조항의 의미를 검사한다.
|
|
227
229
|
#
|
|
228
230
|
# 바이트 단위로 비교하지 않는다 — 손으로 복사하는 블록이라 공백·줄바꿈이 흔들리고,
|
|
229
231
|
# 그 흔들림으로 디스패치를 막으면 규칙이 아니라 서식을 강제하게 된다. 세 항목이
|
|
@@ -247,6 +249,8 @@ def _validate_output_contract_block(text: str) -> list[str]:
|
|
|
247
249
|
f"`{_OUTPUT_CONTRACT_HEADING}` block is required "
|
|
248
250
|
"(convergence.md §\"Required reverify output contract\")"
|
|
249
251
|
]
|
|
252
|
+
if text.count(_OUTPUT_CONTRACT_HEADING) != 1:
|
|
253
|
+
return ["exactly one Output Contract block is required"]
|
|
250
254
|
tail = text.split(_OUTPUT_CONTRACT_HEADING, 1)[1]
|
|
251
255
|
missing = [
|
|
252
256
|
label
|
|
@@ -313,6 +317,42 @@ def validate_reverify_prompt(
|
|
|
313
317
|
return errors
|
|
314
318
|
|
|
315
319
|
|
|
320
|
+
def complete_reverify_instruction(
|
|
321
|
+
body: str, *, task_type: str, forbidden_actions: str,
|
|
322
|
+
model: str, output_contract: str,
|
|
323
|
+
) -> str:
|
|
324
|
+
"""수기 입력의 일치하는 경계는 유지하고 빠진 실행 계약만 생성한다."""
|
|
325
|
+
prefix = []
|
|
326
|
+
models = _header_values(body, MODEL_HEADER)
|
|
327
|
+
if not models:
|
|
328
|
+
prefix.append(f"{MODEL_HEADER} {model}")
|
|
329
|
+
elif _validate_model_header(body, model):
|
|
330
|
+
raise ValueError(f"Model conflicts: expected {model!r}")
|
|
331
|
+
tasks = _header_values(body, TASK_TYPE_HEADER)
|
|
332
|
+
if not tasks:
|
|
333
|
+
prefix.append(f"{TASK_TYPE_HEADER} {task_type}")
|
|
334
|
+
elif tasks != [task_type]:
|
|
335
|
+
raise ValueError(f"Task Type conflicts: expected {task_type!r}, got {tasks!r}")
|
|
336
|
+
actions = _section_values(body, FORBIDDEN_ACTIONS_HEADER)
|
|
337
|
+
if any(_header_values(body, FORBIDDEN_ACTIONS_HEADER)):
|
|
338
|
+
raise ValueError(f"Forbidden actions conflicts: put the exact block after {FORBIDDEN_ACTIONS_HEADER}")
|
|
339
|
+
if not actions:
|
|
340
|
+
prefix.extend([FORBIDDEN_ACTIONS_HEADER, forbidden_actions.strip()])
|
|
341
|
+
elif actions != [forbidden_actions.strip()]:
|
|
342
|
+
raise ValueError(f"Forbidden actions conflicts: expected {forbidden_actions.strip()!r}, got {actions!r}")
|
|
343
|
+
if prefix:
|
|
344
|
+
# 금지 목록 뒤의 일반 문장이 목록 값에 합쳐지지 않도록 경계를 만든다.
|
|
345
|
+
if not body.lstrip().startswith(("## ", "**")):
|
|
346
|
+
body = "## Instructions\n\n" + body
|
|
347
|
+
body = "\n".join(prefix) + "\n\n" + body
|
|
348
|
+
if _OUTPUT_CONTRACT_HEADING not in body:
|
|
349
|
+
body = body.rstrip() + "\n\n" + output_contract.strip() + "\n"
|
|
350
|
+
errors = _validate_output_contract_block(body)
|
|
351
|
+
if errors:
|
|
352
|
+
raise ValueError("; ".join(errors))
|
|
353
|
+
return body
|
|
354
|
+
|
|
355
|
+
|
|
316
356
|
def _task_instructions_offset(text: str) -> int:
|
|
317
357
|
"""Where the lead's own instruction body starts.
|
|
318
358
|
|
|
@@ -150,9 +150,23 @@ def worker_prompt_headers(
|
|
|
150
150
|
)
|
|
151
151
|
if _string_value(manifest.get("taskType")) == "final-verification":
|
|
152
152
|
headers.extend(_final_verification_target_headers(active_context))
|
|
153
|
+
else:
|
|
154
|
+
headers.extend(source_worktree_headers(active_context))
|
|
153
155
|
return headers
|
|
154
156
|
|
|
155
157
|
|
|
158
|
+
def source_worktree_headers(active_context: Mapping[str, Any]) -> list[str]:
|
|
159
|
+
worktree = active_context.get("executorWorktree")
|
|
160
|
+
path = _string_value(worktree.get("path")) if isinstance(worktree, Mapping) else ""
|
|
161
|
+
if not path:
|
|
162
|
+
return []
|
|
163
|
+
return [
|
|
164
|
+
f"**Worktree:** {path}",
|
|
165
|
+
"Read project source from Worktree; resolve .okstra artifacts "
|
|
166
|
+
"and Result Path against Project Root.",
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
|
|
156
170
|
def _worker_provider(manifest: Mapping[str, Any], worker_id: str) -> str:
|
|
157
171
|
"""이 워커를 실행할 공급자.
|
|
158
172
|
|
|
@@ -125,12 +125,12 @@ PHASE_RULES: dict[str, dict[str, str]] = {
|
|
|
125
125
|
"allowed": (
|
|
126
126
|
" - acceptance verdict with requirement coverage assessment\n"
|
|
127
127
|
" - residual risk and regression notes\n"
|
|
128
|
-
" - routing recommendation as the `routingRecommendation` object `{target, rationale}`: `target` is one of `release-handoff`, `release-handoff(stage-group)`, `error-analysis`, `implementation-option-selection`, `implementation-planning`, `implementation`, `done`, and `rationale` ties that choice to the verdict and the blocker list. Both `release-handoff` forms require
|
|
128
|
+
" - routing recommendation as the `routingRecommendation` object `{target, rationale}`: `target` is one of `release-handoff`, `release-handoff(stage-group)`, `error-analysis`, `implementation-option-selection`, `implementation-planning`, `implementation`, `done`, and `rationale` ties that choice to the verdict and the blocker list. Both `release-handoff` forms require a release-ready verdict — `accepted`, or `conditional-accept` with every condition declaring `blocksReleaseHandoff: false` (`okstra_ctl.release_gate.release_handoff_allowed`); plain `release-handoff` additionally requires `whole-task` verification scope. Prose alone does not route the task — Phase 7 projects `workflow.nextRecommendedPhase` from `target`"
|
|
129
129
|
),
|
|
130
130
|
},
|
|
131
131
|
"release-handoff": {
|
|
132
132
|
"allowed": (
|
|
133
|
-
" - entering this phase only when the cited final-verification report's `Verdict Token`
|
|
133
|
+
" - entering this phase only when the cited final-verification report's verdict is release-ready — `Verdict Token` exactly `accepted`, or exactly `conditional-accept` with every Conditional Acceptance Condition row declaring `blocksReleaseHandoff: false` (`okstra_ctl.release_gate.release_handoff_allowed`)\n"
|
|
134
134
|
" - asking the user (via `AskUserQuestion` / interactive prompt) which delivery action to take: `local checkout`, `push + PR`, or `skip` (end the run)\n"
|
|
135
135
|
" - asking the user to pick a PR base branch from `staging` | `preprod` | `prod` | `main` | `dev` | a user-supplied branch name\n"
|
|
136
136
|
" - drafting PR title and PR body **inline as the Okstra lead** (no drafter worker and no delegated dispatch); the lead reviews its own draft with the user before any mutating git / gh command runs\n"
|
|
@@ -58,6 +58,7 @@ from .git_ops import (
|
|
|
58
58
|
is_git_work_tree,
|
|
59
59
|
main_worktree_path,
|
|
60
60
|
merge_branch,
|
|
61
|
+
move_worktree,
|
|
61
62
|
remove_worktree_force,
|
|
62
63
|
)
|
|
63
64
|
from .naming import compute_branch_name, compute_worktree_path
|
|
@@ -87,6 +88,7 @@ __all__ = [
|
|
|
87
88
|
"preview_worktree_decision",
|
|
88
89
|
"provision_stage_worktree",
|
|
89
90
|
"provision_task_worktree",
|
|
91
|
+
"move_worktree",
|
|
90
92
|
"remove_worktree_force",
|
|
91
93
|
"resolve_stage_worktree_decision",
|
|
92
94
|
]
|
|
@@ -40,10 +40,13 @@ def okstra_clean_gate_excludes(project_root: Optional[Path] = None) -> tuple[str
|
|
|
40
40
|
def nested_worktree_excludes(worktree_path) -> tuple[str, ...]:
|
|
41
41
|
"""Relative paths of git worktrees nested under `worktree_path`.
|
|
42
42
|
|
|
43
|
-
implementation stage worktrees live inside the task worktree
|
|
44
|
-
(`<task-id>/stage-<N>/`), so the parent's `git status --short`
|
|
45
|
-
each as an untracked `?? stage-N/`.
|
|
46
|
-
|
|
43
|
+
implementation stage worktrees used to live inside the task worktree
|
|
44
|
+
(`<task-id>/stage-<N>/`), so the parent's `git status --short` reported
|
|
45
|
+
each as an untracked `?? stage-N/`. New stage worktrees are siblings
|
|
46
|
+
(`<task-id>--stage-<N>`, `worktree.naming`) and whole-task
|
|
47
|
+
final-verification prepare moves the remaining nested ones out, so this
|
|
48
|
+
normally returns an empty tuple now — it stays because a task that has not
|
|
49
|
+
been through that prepare still has the nested layout. Derived from
|
|
47
50
|
`git worktree list` rather than a hardcoded pattern so it tracks exactly
|
|
48
51
|
what git registered.
|
|
49
52
|
"""
|
|
@@ -157,6 +157,13 @@ def merge_branch(cwd, branch: str, *, no_ff: bool) -> list[str] | None:
|
|
|
157
157
|
return conflicts
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
def move_worktree(cwd, worktree_path, new_path) -> subprocess.CompletedProcess:
|
|
161
|
+
"""등록된 worktree 를 다른 경로로 옮긴다(`git worktree move`). 내용·브랜치·
|
|
162
|
+
등록은 그대로고 경로만 바뀐다. 중첩 stage worktree 를 task worktree 밖으로
|
|
163
|
+
꺼내는 단일 지점이다."""
|
|
164
|
+
return _git(Path(cwd), "worktree", "move", str(worktree_path), str(new_path))
|
|
165
|
+
|
|
166
|
+
|
|
160
167
|
def remove_worktree_force(cwd, worktree_path) -> subprocess.CompletedProcess:
|
|
161
168
|
"""워크트리를 강제 제거한다. provision_*_worktree 가 워크트리에 .okstra·
|
|
162
169
|
synced 디렉터리/파일 symlink(untracked) 를 깔기 때문에 plain remove 는 그
|
|
@@ -50,10 +50,19 @@ def compute_worktree_path(
|
|
|
50
50
|
stage_number: Optional[int] = None,
|
|
51
51
|
group_id: Optional[str] = None,
|
|
52
52
|
) -> Path:
|
|
53
|
-
"""Pure path computation. One worktree dir per task-key, or
|
|
54
|
-
`<task-key
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
"""Pure path computation. One worktree dir per task-key, or the sibling
|
|
54
|
+
`<task-key>--stage-<N>` when stage_number is given (implementation stage
|
|
55
|
+
isolation). Uses `OKSTRA_HOME` when set (test hook), else `~/.okstra`.
|
|
56
|
+
|
|
57
|
+
stage worktree 는 task worktree 의 *형제* 다. 종전에는 `<task>/stage-<N>/`
|
|
58
|
+
로 안에 두었는데, 그 트리는 task worktree 의 `git status` 에 `?? stage-N/`
|
|
59
|
+
로 잡히고 프로젝트의 prettier·tsc 가 소스로 읽었다 — 실측(2026-09-06,
|
|
60
|
+
fontsninja-v3-site final-verification 001): `pnpm prettier:check` 가
|
|
61
|
+
`stage-1/.next/**` 1,107건으로, `pnpm build` 가 `stage-1/src/**` 의 중복
|
|
62
|
+
전역 선언으로 실패해 두 검증자가 이를 acceptance blocker 로 합의했다.
|
|
63
|
+
whole-task final-verification 은 판정 뒤까지 stage worktree 를 남기므로
|
|
64
|
+
(`stage_targets`, `teardown=False`) 검증 대상 트리에서 그 디렉터리가 사라질
|
|
65
|
+
길이 없었다. 형제 배치는 git·도구·변이 감사 모두에게 같은 답이 된다."""
|
|
57
66
|
if stage_number is not None and group_id is not None:
|
|
58
67
|
raise ValueError("stage_number and group_id are mutually exclusive")
|
|
59
68
|
base = okstra_home()
|
|
@@ -64,9 +73,9 @@ def compute_worktree_path(
|
|
|
64
73
|
/ _safe_segment(task_id_segment)
|
|
65
74
|
)
|
|
66
75
|
if stage_number is not None:
|
|
67
|
-
path = path
|
|
76
|
+
path = path.with_name(f"{path.name}--stage-{stage_number}")
|
|
68
77
|
if group_id is not None:
|
|
69
|
-
path = path
|
|
78
|
+
path = path.with_name(f"{path.name}--group-{_safe_segment(group_id)}")
|
|
70
79
|
return path
|
|
71
80
|
|
|
72
81
|
|
|
@@ -273,7 +273,8 @@ def provision_stage_worktree(
|
|
|
273
273
|
|
|
274
274
|
Unlike `provision_task_worktree` (one worktree per task-key shared
|
|
275
275
|
across phases), this provisions a per-stage worktree branched from
|
|
276
|
-
`base_commit` at `<task-key
|
|
276
|
+
`base_commit` at `<task-key>--stage-<N>` (a sibling of the task worktree) on
|
|
277
|
+
branch `<prefix>-<task>-s<N>`.
|
|
277
278
|
The stage-key (`<task-key>#stage-<N>`) is reserved atomically through
|
|
278
279
|
`worktree_registry`; re-entry of the same stage-key returns the
|
|
279
280
|
existing entry. Branch / on-disk conflicts roll back the worktree
|
|
@@ -177,6 +177,33 @@ def reserve(
|
|
|
177
177
|
return WorktreeEntry(task_key=key, **row)
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
def relocate_worktree(
|
|
181
|
+
project_id: str, task_group: str, task_id: str, *,
|
|
182
|
+
stage_number: Optional[int], worktree_path: str,
|
|
183
|
+
) -> dict:
|
|
184
|
+
"""Record that a registered worktree now lives at `worktree_path`.
|
|
185
|
+
|
|
186
|
+
`git worktree move` 가 디스크와 git 등록을 옮긴 뒤 호출한다. 이전 경로는
|
|
187
|
+
`relocated_from` 에 누적해 남긴다 — 옮긴 사실이 사라지면 옛 경로를 적은
|
|
188
|
+
산출물(런 매니페스트, 프롬프트)을 읽는 쪽이 그 디렉터리를 찾지 못하는
|
|
189
|
+
이유를 알 수 없다. 행이 없으면 RuntimeError.
|
|
190
|
+
"""
|
|
191
|
+
key = task_key(project_id, task_group, task_id, stage_number)
|
|
192
|
+
with _registry_lock():
|
|
193
|
+
data = _load()
|
|
194
|
+
row = data["tasks"].get(key)
|
|
195
|
+
if not row:
|
|
196
|
+
raise RuntimeError(f"worktree registry has no row for {key}")
|
|
197
|
+
previous = row.get("worktree_path", "")
|
|
198
|
+
if previous != worktree_path:
|
|
199
|
+
history = list(row.get("relocated_from") or [])
|
|
200
|
+
history.append(previous)
|
|
201
|
+
row["relocated_from"] = history
|
|
202
|
+
row["worktree_path"] = worktree_path
|
|
203
|
+
_save(data)
|
|
204
|
+
return dict(row)
|
|
205
|
+
|
|
206
|
+
|
|
180
207
|
def touch_phase(project_id: str, task_group: str, task_id: str, phase: str) -> None:
|
|
181
208
|
"""Record the most recent phase observed on this worktree.
|
|
182
209
|
Best-effort: silently no-ops if the task-key is not registered.
|
|
@@ -17,6 +17,9 @@ class WrapperStatus:
|
|
|
17
17
|
timeout: bool
|
|
18
18
|
log_path: Path | None
|
|
19
19
|
raw: Mapping[str, Any]
|
|
20
|
+
# wrapper 가 init 에 기록한 자기 pid (`okstra-wrapper-status.py init`). 상태
|
|
21
|
+
# 파일을 못 쓰고 죽은 프로세스를 await 가 알아보는 유일한 단서다.
|
|
22
|
+
pid: int | None = None
|
|
20
23
|
|
|
21
24
|
@property
|
|
22
25
|
def is_terminal(self) -> bool:
|
|
@@ -82,6 +85,7 @@ def read_wrapper_status(path: Path) -> WrapperStatus | None:
|
|
|
82
85
|
timeout=raw.get("timeout") is True,
|
|
83
86
|
log_path=_optional_path(raw.get("log_path")),
|
|
84
87
|
raw=raw,
|
|
88
|
+
pid=_optional_int(raw.get("pid")),
|
|
85
89
|
)
|
|
86
90
|
|
|
87
91
|
|
|
@@ -222,6 +222,11 @@ Order of operations:
|
|
|
222
222
|
questions into one. When you relay this preflight, tell the user in one
|
|
223
223
|
line *why* task group comes first (it scopes where this ticket's brief —
|
|
224
224
|
and any sub-ticket briefs — are written).
|
|
225
|
+
|
|
226
|
+
**Enforced:** `validators/validate-brief.py` requires the brief's
|
|
227
|
+
directory segment to equal the slugified frontmatter `task-group`, so a
|
|
228
|
+
brief written before the group is known — or under an invented placeholder
|
|
229
|
+
group — fails validation rather than landing in the wrong tree.
|
|
225
230
|
1. `AskUserQuestion` (free text):
|
|
226
231
|
`"Ticket key or URL (e.g. LIN-1234, PROJ-42, https://linear.app/..., https://your.atlassian.net/browse/...)"` →
|
|
227
232
|
`ticket_ref`. Issue this only **after** Step 2a's task-group answer is in
|
|
@@ -451,6 +456,8 @@ to Source Material body, not to the filename):
|
|
|
451
456
|
Validate the full `<ticket-id>-<file-title>` slug: must have at least one
|
|
452
457
|
alphanumeric character after slugification. Apply Step 2c on collision.
|
|
453
458
|
|
|
459
|
+
**Enforced:** `validators/validate-brief.py` checks the brief's filename and its task-group directory segment against the slugified frontmatter `task-group`, and rejects a Source Material section whose entry does not match the source it names.
|
|
460
|
+
|
|
454
461
|
### 2c. Collision handling
|
|
455
462
|
|
|
456
463
|
If a brief file already exists, `AskUserQuestion` with (`Skip` /
|
|
@@ -546,6 +553,8 @@ empty or trivially thin **after** reading Source Material verbatim, ask **at
|
|
|
546
553
|
most one** `AskUserQuestion` to fill it. Never ask about sections already
|
|
547
554
|
covered by the source material.
|
|
548
555
|
|
|
556
|
+
**Enforced:** `validators/validate-brief.py` fails a brief whose required section is missing, is left as a template placeholder (`is_placeholder`), or carries only the template's own example lines (`is_template_example`) — so a section skipped here does not pass as filled.
|
|
557
|
+
|
|
549
558
|
### Sharpening pass (bounded grill)
|
|
550
559
|
|
|
551
560
|
The following rules absorb the useful parts of `grill-me` /
|
|
@@ -656,6 +665,8 @@ labels. Unlabelled augmentation is rejected as half-formed translation.
|
|
|
656
665
|
**Inline form** — `> augmented: intent-inference — <one line>`.
|
|
657
666
|
**Section form** — `- intent-inference: <one line>` under `Augmentation`.
|
|
658
667
|
|
|
668
|
+
**Enforced:** `validators/validate-brief.py` reads both forms (`inline_augmented_blockquotes`, `augmentation_entries`) and fails a brief whose entry carries a label outside this table (`parse_augmentation_label`).
|
|
669
|
+
|
|
659
670
|
### Auto-mirroring rule
|
|
660
671
|
|
|
661
672
|
Every `intent-inference` entry must have a paired entry in `Open Questions`
|
|
@@ -1014,13 +1025,21 @@ per-page ratio instead of origin load). If the file is absent, ask once via
|
|
|
1014
1025
|
|
|
1015
1026
|
Never fill the skeleton yourself from the tickets: a ticket summary decides
|
|
1016
1027
|
nothing, and the sections ask for what the tickets do not say. If the file
|
|
1017
|
-
exists, leave it untouched and mention its path in the hand-off block.
|
|
1028
|
+
exists, leave it untouched and mention its path in the hand-off block. The
|
|
1029
|
+
file's trailing `## Task Memory` region (between `<!-- okstra:task-memory:begin -->`
|
|
1030
|
+
and `end`) is okstra's: `report-finalize` rewrites it after every run with the
|
|
1031
|
+
group's start order and each task's latest conclusion, and a group whose
|
|
1032
|
+
runs finished before anyone created the file already has one holding that
|
|
1033
|
+
region alone — `init` then inserts the human sections above it.
|
|
1018
1034
|
|
|
1019
1035
|
Then stop. Do not invoke `okstra-run` directly — the user chooses when to
|
|
1020
1036
|
proceed, and they may want to edit the brief externally first. In the
|
|
1021
1037
|
multi-brief case the user also decides the order in which tasks are
|
|
1022
|
-
started
|
|
1023
|
-
|
|
1038
|
+
started; the brief ordinal in each brief id (`<TICKET>-<n>-<slug>`) is what
|
|
1039
|
+
okstra reads as the group's start order (`Related Task Graph` edges are
|
|
1040
|
+
quoted beside it as `waits for`, never reordering), so number the briefs in
|
|
1041
|
+
the order the user wants them started. When a
|
|
1042
|
+
skeleton was created in 7a, add one line to the hand-off block:
|
|
1024
1043
|
|
|
1025
1044
|
```
|
|
1026
1045
|
group context skeleton: <abs>/<task-group>/group-context.md (fill before /okstra-run)
|
|
@@ -48,10 +48,12 @@ If the host has a native picker, use it. Otherwise print a numbered list.
|
|
|
48
48
|
1. Run `okstra chat rooms`.
|
|
49
49
|
2. If the output is `no rooms`, say so and offer create.
|
|
50
50
|
3. Otherwise pick a room from that list (picker, or numbered list if the picker limit is exceeded).
|
|
51
|
-
4.
|
|
52
|
-
5.
|
|
51
|
+
4. Run `okstra chat members --room <room>`.
|
|
52
|
+
5. Ask for the display name as free input.
|
|
53
|
+
6. If that name is not in the members output, run `okstra chat join --room <room> --name <display>`.
|
|
54
|
+
7. If that name is already in the members output, ask one question: is this session already in the room under that name (for example, it joined before the conversation was summarized)? If yes, do not run join; continue with `--as <display>`. If no, ask for a different display name and go back to 6.
|
|
53
55
|
|
|
54
|
-
`--name` is required. An empty name, `all`, or a name already in the room fails.
|
|
56
|
+
`--name` is required. An empty name, `all`, or a name already in the room fails. `display name already taken` after a context loss means step 7, not a new name.
|
|
55
57
|
|
|
56
58
|
## Step 2: Unread
|
|
57
59
|
|
|
@@ -59,7 +61,7 @@ If the host has a native picker, use it. Otherwise print a numbered list.
|
|
|
59
61
|
okstra chat unread --room <room> --as <display>
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
Show the rows. Each row is `id @from YYYY-MM-DD HH:MM body`. Sender is always `@name`. The id is the first token (before the first space). A reply inserts `↑<parentId>` after the time. Recipient is not on the line.
|
|
64
|
+
Show the rows. Each row is `id @from YYYY-MM-DD HH:MM body`. Sender is always `@name`. The id is the first token (before the first space). A reply inserts `↑<parentId>` after the time. Recipient is not on the line. A body with several lines continues on the following rows; those rows start with two spaces and carry no id.
|
|
63
65
|
|
|
64
66
|
If the output is `no unread`, do not ack.
|
|
65
67
|
|
|
@@ -69,7 +71,7 @@ If there are unread rows, always run:
|
|
|
69
71
|
okstra chat ack --room <room> --as <display> --through <id>
|
|
70
72
|
```
|
|
71
73
|
|
|
72
|
-
Use the last
|
|
74
|
+
Use the id of the last row that starts with an id. CLI unread does not move the cursor; this ack does. Ack never moves the cursor backwards.
|
|
73
75
|
|
|
74
76
|
## Step 3: Next action
|
|
75
77
|
|
|
@@ -81,7 +83,7 @@ There is no ack menu item. Choosing unread again shows the rows then acks, same
|
|
|
81
83
|
|
|
82
84
|
1. Run `okstra chat members --room <room>`.
|
|
83
85
|
2. Pick the recipient from `all` plus those names except the current display name (`--as`). Filter after `okstra chat members`. Do not pass `--as` to `members`. Recipient is required.
|
|
84
|
-
3. Ask for the body as free input.
|
|
86
|
+
3. Ask for the body as free input. The body may span several lines; pass it as one `--body` argument.
|
|
85
87
|
4. Run `okstra chat send --room <room> --as <display> --to <all|name> --body <text>`.
|
|
86
88
|
|
|
87
89
|
### Reply
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: okstra-inspect
|
|
3
3
|
description: >-
|
|
4
|
-
Use this for everything that happens AFTER
|
|
4
|
+
Use this for everything that happens AFTER an okstra task has already run — inspecting it or light bookkeeping, never launching new work. The tell is usually a named task id (PROD-1623, dev-9184) without the word "okstra." Reach for it when the user wants one task's: status, current/next phase, blockers, or approval gate; its final report — where it is or whether it passed; its elapsed time or context/read cost; its run history, re-run, or resume; to mark it done / in-progress / blocked / todo; or a failed run's error logs gathered into a report. The recap facet also takes a task-group: which briefs are done / in progress / not started, what is next, each task's latest conclusion. Also builds an anonymized cross-project error zip and audits run health. NOT for starting a run (okstra-run), a group's run/time/error totals (okstra-rollup), schedules (okstra-schedule-gen), a brief (okstra-brief-gen), setup (okstra-setup), or cross-project management (okstra-manager).
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# OKSTRA Inspect
|
|
@@ -19,7 +19,7 @@ Single read-side entry point for okstra runtime inspection plus the one status m
|
|
|
19
19
|
| `errors` | `facets/errors.md` | Aggregate okstra-run error logs for a task into a timestamped markdown report; print a summary. |
|
|
20
20
|
| `error-zip` | `facets/error-zip.md` | Collect cross-project okstra error logs into an anonymized zip (report + raw) and summarize clusters. |
|
|
21
21
|
| `run-audit` | `facets/run-audit.md` | Check every run's artifacts against progress invariants; report what went wrong without an error ever being logged. |
|
|
22
|
-
| `recap` | `facets/recap.md` | Summarize a task's run-to-run phase transitions, then answer free-form questions over
|
|
22
|
+
| `recap` | `facets/recap.md` | Summarize a task's run-to-run phase transitions — or a task-group's start order, per-brief status, and each task's latest conclusion — then answer free-form questions over the `.okstra` artifacts. Appends each summary/Q&A to `recap-log.jsonl` (task `recap/`, group `.recap/`), and writes agent-authored notes to `notes/` for feeding into later runs. |
|
|
23
23
|
|
|
24
24
|
## Step 0: Preflight (shared)
|
|
25
25
|
|
|
@@ -6,13 +6,16 @@ Loaded lazily by the dispatch table in `SKILL.md` (core). Shared rules — Step
|
|
|
6
6
|
|
|
7
7
|
Trigger phrases: "okstra recap", "recap", "work summary", "summarize this task", "before/after summary", "explain this work", "task question".
|
|
8
8
|
|
|
9
|
-
On top of the `.okstra` artifacts accumulated for a single task-id
|
|
9
|
+
On top of the `.okstra` artifacts accumulated for a single task-id — or for every task of one task-group — (a) produce a before/after summary and (b) answer free-form questions about that work. By default it reads only the `.okstra/` subtree (artifact mode). It expands to code mode only when the user explicitly asks to look at the code changes too. This sub-command performs only the `recap-log.jsonl` append and the `notes/` note authoring (recap.5); it never mutates `task-manifest.json` / catalog / timeline / `group-context.md`.
|
|
10
10
|
|
|
11
11
|
### recap.1 — Resolve target
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Two scopes. Decide the scope before resolving anything:
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
- **Task scope** — the same three forms as `cost` / `errors`: ① full task-key, ② bare token — the standard task-key resolution rule in `SKILL.md` (core), ③ task-root path.
|
|
16
|
+
- **Group scope** — the user names a task-group ("recap the cache group", "how far is the `cache` group", "group progress") or the bare token resolves with every match `Matched via: taskGroup`. Do **not** drop to the N-match picker in that case; carry the group name as `<task-group>` and follow recap.2g / recap.4g below. A group-scope request still takes the one-task path when the user picks a single task from it.
|
|
17
|
+
|
|
18
|
+
If the user asks for a recap without naming a task or a group (e.g. "summarize this work"), apply the core no-task fallback as-is — do not list only a placeholder form like `<task-group>` and ask back.
|
|
16
19
|
|
|
17
20
|
### recap.2 — Assemble the before/after summary
|
|
18
21
|
|
|
@@ -28,11 +31,27 @@ Read the fixed-text `Run count` and repeated `Transition` blocks. Narrate each b
|
|
|
28
31
|
|
|
29
32
|
For a transition that has a `Report`, run `okstra render-final-report <Report>` and read the rendered Markdown only when the user wants a deeper summary. Do not open the report data JSON directly and do not render every report automatically.
|
|
30
33
|
|
|
34
|
+
### recap.2g — Assemble the group status (group scope)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
okstra model-io recap-input --project-root <projectRoot> --task-group <task-group>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The projection joins three sources and is the only place to read them from — do not re-derive the queue from the brief directory, the catalog, or `group-context.md` by hand:
|
|
41
|
+
|
|
42
|
+
- the group's briefs give the start order (brief ordinal) and the `waits for` edges;
|
|
43
|
+
- the catalog / each `task-manifest.json` gives the real `Status` (`done` / `in progress` / `not started`), `Progress` (`<phase> (<state>)`), run count, next phase, and report path;
|
|
44
|
+
- the group document's Task Memory gives each recorded task's `Headline`, `Decision N`, `Watch out N`, `Follow-up N`. A task with `Memory run: -` finished before group memory existed — its conclusion lives only in its `Report`.
|
|
45
|
+
|
|
46
|
+
Read the head lines (`Brief count`, `Task count`, `Next in group`), then the repeated `Queue entry` blocks in order, then the repeated `Task` blocks. Narrate the group as: how many briefs, how many started, which are done, which is next (`Next in group` — `-` means every brief has started), and any `Waits for` that names a ticket not yet done. `Human sections: absent` means the group has no authored `group-context.md` context yet (only okstra's memory region) — say so once when the user asks why the group exists; do not offer to write it here (that is `okstra group-context init` via okstra-brief-gen).
|
|
47
|
+
|
|
48
|
+
Numbers beyond run count (CPU, wall clock, error totals) are okstra-rollup's; if the user asks for them, point to `/okstra-rollup --task-group <task-group>` instead of summing anything here.
|
|
49
|
+
|
|
31
50
|
### recap.3 — Free-form Q&A loop
|
|
32
51
|
|
|
33
52
|
After the summary output, take the user's free-form questions.
|
|
34
53
|
|
|
35
|
-
- **artifact mode (default)**: answer only from `.okstra/` artifacts (timeline, task-manifest, that run's final-report). Every factual claim is accompanied by a `path:line` citation to the `.okstra` file.
|
|
54
|
+
- **artifact mode (default)**: answer only from `.okstra/` artifacts (timeline, task-manifest, that run's final-report; in group scope also `.okstra/briefs/<task-group>/group-context.md` and each member task's artifacts). Every factual claim is accompanied by a `path:line` citation to the `.okstra` file.
|
|
36
55
|
- **code mode (opt-in)**: only when the user **explicitly asks** — "look at the code changes / the diff too" — additionally read `git diff` · `git log` from that task's worktree/branch and answer at the code level. Entering recap never by itself switches to code mode.
|
|
37
56
|
|
|
38
57
|
### recap.4 — Persist each turn
|
|
@@ -48,6 +67,19 @@ okstra recap record <resolved-target> --project-root <projectRoot> \
|
|
|
48
67
|
|
|
49
68
|
Put a one-or-two-line summary in `--answer`, not the full answer transcript. If there are multiple citations, repeat `--citation`. Do not silently swallow a recording failure (abnormal exit) — tell the user.
|
|
50
69
|
|
|
70
|
+
### recap.4g — Persist each turn (group scope)
|
|
71
|
+
|
|
72
|
+
Same flags, but the group replaces the target and the log lands at `.okstra/tasks/<task-group>/.recap/recap-log.jsonl`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
okstra recap record --task-group <task-group> --project-root <projectRoot> \
|
|
76
|
+
--kind <summary|qa> --mode <artifact|code> \
|
|
77
|
+
--question "<question or omit>" --answer "<one or two line summary>" \
|
|
78
|
+
--citation "<path:line>"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`recap note` has no group form — a note belongs to the task whose next run will carry it, so write it against that task-key.
|
|
82
|
+
|
|
51
83
|
### recap — Output template
|
|
52
84
|
|
|
53
85
|
```markdown
|
|
@@ -62,6 +94,22 @@ Before/after summary (runs: <N>):
|
|
|
62
94
|
<Free-form questions follow. To include code changes, ask "also show me the diff".>
|
|
63
95
|
```
|
|
64
96
|
|
|
97
|
+
Group scope:
|
|
98
|
+
|
|
99
|
+
```markdown
|
|
100
|
+
## okstra Recap — task-group <task-group> (briefs: <Brief count>, started: <Task count>)
|
|
101
|
+
|
|
102
|
+
| # | task | status | progress | waits for | latest conclusion |
|
|
103
|
+
|---|---|---|---|---|---|
|
|
104
|
+
| 1 | dev-10626-1-… | done | release-handoff (completed) | — | <Headline, or "report only" when Memory run is -> |
|
|
105
|
+
| 2 | dev-10627-2-… | in progress | implementation-option-selection (prepared) | DEV-10642 | <Headline> |
|
|
106
|
+
| 3 | dev-10628-3-… | not started | — | — | — |
|
|
107
|
+
|
|
108
|
+
Next in group: <Next in group or "every brief has started">
|
|
109
|
+
|
|
110
|
+
<Free-form questions follow.>
|
|
111
|
+
```
|
|
112
|
+
|
|
65
113
|
### recap.5 — Agent-authored notes (`notes/`)
|
|
66
114
|
|
|
67
115
|
Leave something in `notes/` only when, during recap, you produced an artifact **for the okstra task** — verification evidence, a design/decision draft, an analysis note — that may serve as grounding for future runs, and whose content is **neither a rendered report nor a user decision**.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: okstra-rollup
|
|
3
3
|
description: |
|
|
4
|
-
Use when the user wants run results from MULTIPLE okstra tasks collected and summarized at once — a task-group digest or a whole-project roll-up, not a single task. Aggregates per-task run count, elapsed time, error count, and latest report path, plus group-level totals and status/category/phase tallies, then synthesizes a cross-task prose summary from the report files. Make sure to use this skill whenever the user mentions "okstra rollup", "rollup", "task-group summary", "group-level report", "collect multiple task results", "whole-project task status summary", "cross-task summary", "consolidated group report", "organize all tasks", "run results all at once", even if they don't say "rollup". For a SINGLE task's report/time/errors/recap use okstra-inspect
|
|
4
|
+
Use when the user wants run results from MULTIPLE okstra tasks collected and summarized at once — a task-group digest or a whole-project roll-up, not a single task. Aggregates per-task run count, elapsed time, error count, and latest report path, plus group-level totals and status/category/phase tallies, then synthesizes a cross-task prose summary from the report files. Make sure to use this skill whenever the user mentions "okstra rollup", "rollup", "task-group summary", "group-level report", "collect multiple task results", "whole-project task status summary", "cross-task summary", "consolidated group report", "organize all tasks", "run results all at once", even if they don't say "rollup". For a SINGLE task's report/time/errors/recap, or "where does the group stand" (per-brief status, what is next), use okstra-inspect; this skill is for the numbers and the cross-task digest. For a forward-looking work plan over non-done tasks use okstra-schedule-gen.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# OKSTRA Rollup
|