okstra 0.218.0 → 0.219.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/cli-registry.mjs +0 -10
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/lifecycle/config.mjs +2 -3
- package/dist/commands/lifecycle/config.mjs.map +1 -1
- package/dist/commands/pr/pr.d.mts +6 -0
- package/dist/commands/pr/pr.mjs +58 -0
- package/dist/commands/pr/pr.mjs.map +1 -1
- package/dist/lib/runtime-payload.mjs +1 -2
- package/dist/lib/runtime-payload.mjs.map +1 -1
- package/docs/architecture/storage-model.md +1 -1
- package/docs/architecture.md +9 -7
- package/docs/cli.md +3 -4
- package/docs/project-structure-overview.md +4 -5
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -3
- package/runtime/prompts/lead/report-writer.md +10 -17
- package/runtime/prompts/lead/team-contract.md +1 -2
- package/runtime/prompts/wizard/prompts.ko.json +0 -1
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +1 -1
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +1 -1
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +0 -1
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +1 -1
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +0 -1
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +0 -1
- package/runtime/python/okstra_ctl/agent/invocation.py +1 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +2 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/jobs.py +0 -2
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +2 -27
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +0 -4
- package/runtime/python/okstra_ctl/dispatch_core.py +3 -349
- package/runtime/python/okstra_ctl/dispatch_state.py +0 -13
- package/runtime/python/okstra_ctl/domain/provider.py +1 -1
- package/runtime/python/okstra_ctl/domain/role.py +0 -6
- package/runtime/python/okstra_ctl/final_report_paths.py +0 -16
- package/runtime/python/okstra_ctl/i18n.py +2 -2
- package/runtime/python/okstra_ctl/path_hints.py +0 -4
- package/runtime/python/okstra_ctl/paths.py +0 -3
- package/runtime/python/okstra_ctl/phases/final_verification/profile.md +2 -0
- package/runtime/python/okstra_ctl/phases/final_verification/spec.md +2 -0
- package/runtime/python/okstra_ctl/phases/final_verification/target.py +17 -1
- package/runtime/python/okstra_ctl/phases/implementation/entry.py +12 -0
- package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-deliverable.md +1 -0
- package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-executor.md +1 -0
- package/runtime/python/okstra_ctl/phases/implementation/spec.md +4 -0
- package/runtime/python/okstra_ctl/phases/release_handoff/boundary.json +1 -1
- package/runtime/python/okstra_ctl/phases/release_handoff/operations.py +24 -3
- package/runtime/python/okstra_ctl/phases/release_handoff/profile.md +11 -11
- package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +4 -2
- package/runtime/python/okstra_ctl/render.py +0 -10
- package/runtime/python/okstra_ctl/render_final_report.py +4 -4
- package/runtime/python/okstra_ctl/report_finalize.py +37 -76
- package/runtime/python/okstra_ctl/report_html/render.py +4 -44
- package/runtime/python/okstra_ctl/report_language.py +3 -3
- package/runtime/python/okstra_ctl/report_prose_keys.py +153 -0
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +1 -1
- package/runtime/python/okstra_ctl/run.py +1 -21
- package/runtime/python/okstra_ctl/stage_integrate.py +37 -2
- package/runtime/python/okstra_ctl/stage_rebase.py +189 -0
- package/runtime/python/okstra_ctl/stage_targets.py +29 -62
- package/runtime/python/okstra_ctl/team.py +1 -6
- package/runtime/python/okstra_ctl/wizard/confirmation.py +0 -8
- package/runtime/python/okstra_ctl/wizard/registry.py +2 -3
- package/runtime/python/okstra_ctl/wizard/sources.py +10 -0
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +3 -12
- package/runtime/python/okstra_ctl/worktree/__init__.py +1 -1
- package/runtime/python/okstra_ctl/worktree/decisions.py +3 -4
- package/runtime/python/okstra_ctl/worktree/provision.py +27 -0
- package/runtime/python/okstra_ctl/worktree/rebase.py +108 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +14 -0
- package/runtime/skills/_fragments/wizard-render-bundle.md +1 -1
- package/runtime/skills/okstra-code-review/SKILL.md +1 -1
- package/runtime/skills/okstra-discussion/SKILL.md +1 -1
- package/runtime/skills/okstra-pr-gen/SKILL.md +9 -0
- package/runtime/skills/okstra-run/SKILL.md +1 -1
- package/runtime/skills/okstra-setup/SKILL.md +2 -2
- package/runtime/skills/okstra-setup/references/project-config.md +2 -2
- package/runtime/templates/pr/drafting-rules.md +4 -0
- package/runtime/templates/report-writer-prompt-preamble.md +1 -1
- package/runtime/templates/reports/html/i18n/en.json +0 -1
- package/runtime/templates/reports/html/i18n/ko.json +0 -1
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-run.py +1 -28
- package/runtime/agents/operations/report-translation.json +0 -6
- package/runtime/agents/roles/translator.json +0 -19
- package/runtime/bin/okstra-report-translate.py +0 -404
- package/runtime/prompts/duties/translator.json +0 -32
- package/runtime/python/okstra_ctl/report_translation.py +0 -509
- package/runtime/python/okstra_ctl/report_translation_dispatch.py +0 -356
- package/runtime/templates/translator-prompt-preamble.md +0 -36
|
@@ -13,7 +13,6 @@ PromptAudience = Literal[
|
|
|
13
13
|
"implementation-executor",
|
|
14
14
|
"implementation-verifier",
|
|
15
15
|
"report-writer",
|
|
16
|
-
"translator",
|
|
17
16
|
"reverify",
|
|
18
17
|
"lead-only",
|
|
19
18
|
]
|
|
@@ -84,7 +83,6 @@ WORKER_PREAMBLE_FILENAME_BY_AUDIENCE = {
|
|
|
84
83
|
"implementation-executor": "implementation-worker-preamble.md",
|
|
85
84
|
"implementation-verifier": "implementation-worker-preamble.md",
|
|
86
85
|
"report-writer": "report-writer-prompt-preamble.md",
|
|
87
|
-
"translator": "translator-prompt-preamble.md",
|
|
88
86
|
}
|
|
89
87
|
WORKER_ERROR_CONTRACT_FILENAME = "worker-error-contract.md"
|
|
90
88
|
# coverage-critic gap 검증 라운드의 dispatch kind. 번호 라운드(`reverify-r<N>`)와
|
|
@@ -199,7 +197,7 @@ def resolve_prompt_plan(
|
|
|
199
197
|
"""Resolve prompt policy from the worker's role and the dispatch kind.
|
|
200
198
|
|
|
201
199
|
Neither the worker id nor the provider or model identity takes part. The id
|
|
202
|
-
used to: `report-writer
|
|
200
|
+
used to: `report-writer` and "the executor's worker id" were
|
|
203
201
|
compared by spelling, so a verifier that happened to share the executor's
|
|
204
202
|
id received the executor's prompt.
|
|
205
203
|
"""
|
|
@@ -210,12 +208,6 @@ def resolve_prompt_plan(
|
|
|
210
208
|
# 걸리지 않아 분석 계획으로 떨어졌고, 리드 프롬프트가 분석 워커 직무를
|
|
211
209
|
# 달고 나갔다.
|
|
212
210
|
return _plan("lead-only")
|
|
213
|
-
if dispatch_kind == "translator":
|
|
214
|
-
if role not in (None, "translator"):
|
|
215
|
-
raise ValueError(f"translator dispatch cannot run role {role}")
|
|
216
|
-
return _plan("translator", duty_audience="translator")
|
|
217
|
-
if role == "translator":
|
|
218
|
-
raise ValueError("translator role requires the translator dispatch kind")
|
|
219
211
|
if is_verification_dispatch_kind(dispatch_kind):
|
|
220
212
|
return _plan("reverify")
|
|
221
213
|
# A critic pass keeps the full analysis contract — worker anchor headers, the
|
|
@@ -307,8 +299,8 @@ def resolve_prompt_plan_for_manifest(
|
|
|
307
299
|
"""Resolve policy from the manifest's task type and the worker's roster role.
|
|
308
300
|
|
|
309
301
|
*worker_id* is the lookup key into `workerAssignments`; its spelling decides
|
|
310
|
-
nothing. A worker with no roster row —
|
|
311
|
-
|
|
302
|
+
nothing. A worker with no roster row — a verification pass whose kind
|
|
303
|
+
alone decides — resolves with no role.
|
|
312
304
|
"""
|
|
313
305
|
task_type = _required_string(manifest, "taskType")
|
|
314
306
|
if not worker_id.strip():
|
|
@@ -383,7 +375,6 @@ def _duty_audience(audience: PromptAudience) -> AgentAudience:
|
|
|
383
375
|
"implementation-executor": "implementation-executor",
|
|
384
376
|
"implementation-verifier": "implementation-verifier",
|
|
385
377
|
"report-writer": "report-writer",
|
|
386
|
-
"translator": "translator",
|
|
387
378
|
"reverify": "reverification-worker",
|
|
388
379
|
"lead-only": "lead",
|
|
389
380
|
}[audience]
|
|
@@ -34,7 +34,7 @@ Side effects:
|
|
|
34
34
|
`sync_config`(무엇을 옮길지), `git_ops`(git 사실) — 은 서로를 모른다. 그 위에
|
|
35
35
|
`cleanliness`(무엇이 더러운가) · `linking`(실제로 링크 걸기) ·
|
|
36
36
|
`decisions`(무엇이 일어날지 미리 답하기)가 있고, 부수 효과를 내는 것은
|
|
37
|
-
`provision`
|
|
37
|
+
`provision` 과 단계 브랜치를 재배치하는 `rebase` 뿐이다. 이 파일은 밖으로 나가는 이름만 모아 둔다.
|
|
38
38
|
"""
|
|
39
39
|
from __future__ import annotations
|
|
40
40
|
|
|
@@ -118,10 +118,9 @@ def _stage_entry_is_reusable(entry: worktree_registry.WorktreeEntry) -> bool:
|
|
|
118
118
|
the fix-run case: a stage whose verifier returned FAIL records a `failed`
|
|
119
119
|
consumers row, which frees the occupancy but deliberately keeps the worktree
|
|
120
120
|
and its branch as the reviewable stack. Re-entry MUST reuse that tree —
|
|
121
|
-
provisioning anew refuses on the existing
|
|
122
|
-
|
|
123
|
-
the stage provisions from scratch.
|
|
121
|
+
provisioning anew refuses on the existing branch. A removed directory is
|
|
122
|
+
restored from that branch on the next implementation or verification entry.
|
|
124
123
|
"""
|
|
125
124
|
if entry.status == "active":
|
|
126
125
|
return True
|
|
127
|
-
return entry.status == "released"
|
|
126
|
+
return entry.status == "released"
|
|
@@ -13,6 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
|
|
14
14
|
from .. import worktree_registry
|
|
15
15
|
from .decisions import preview_worktree_decision, resolve_stage_worktree_decision
|
|
16
|
+
from .rebase import rebase_stage_branch
|
|
16
17
|
from .git_ops import (
|
|
17
18
|
_branch_exists,
|
|
18
19
|
_branch_exists_message,
|
|
@@ -259,6 +260,21 @@ def provision_task_worktree(
|
|
|
259
260
|
)
|
|
260
261
|
|
|
261
262
|
|
|
263
|
+
def restore_stage_worktree(project_root: Path, path: str, branch: str) -> None:
|
|
264
|
+
target = Path(path)
|
|
265
|
+
if target.exists():
|
|
266
|
+
return
|
|
267
|
+
root = main_worktree_path(project_root)
|
|
268
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
269
|
+
result = _git(root, "worktree", "add", str(target), branch)
|
|
270
|
+
if result.returncode:
|
|
271
|
+
raise RuntimeError(f"cannot restore stage worktree {target}: {result.stderr.strip()}")
|
|
272
|
+
_link_sync_dirs(root, target)
|
|
273
|
+
_link_sync_files(root, target)
|
|
274
|
+
_copy_snapshot_files(root, target)
|
|
275
|
+
_seed_worktree_settings_symlink(target)
|
|
276
|
+
|
|
277
|
+
|
|
262
278
|
def provision_stage_worktree(
|
|
263
279
|
*,
|
|
264
280
|
project_root: Path,
|
|
@@ -298,6 +314,17 @@ def provision_stage_worktree(
|
|
|
298
314
|
stage_number=stage_number,
|
|
299
315
|
)
|
|
300
316
|
if decision.status == "reused":
|
|
317
|
+
restore_stage_worktree(project_root, decision.path, decision.branch)
|
|
318
|
+
resolved_base = _resolve_commit_sha(project_root, base_commit)
|
|
319
|
+
if not resolved_base:
|
|
320
|
+
raise RuntimeError(f"could not resolve base_commit `{base_commit}`")
|
|
321
|
+
if decision.base_ref != resolved_base:
|
|
322
|
+
rebase_stage_branch(project_root, decision.branch, decision.base_ref, resolved_base)
|
|
323
|
+
worktree_registry.update_stage_base(
|
|
324
|
+
project_id, task_group_segment, task_id_segment, stage_number,
|
|
325
|
+
expected_base=decision.base_ref, base_ref=resolved_base,
|
|
326
|
+
)
|
|
327
|
+
decision.base_ref = resolved_base
|
|
301
328
|
drift = _sync_key_drift_note(
|
|
302
329
|
main_worktree_path(project_root),
|
|
303
330
|
worktree_registry.lookup(
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""단계 브랜치 하나를 새 기준 위로 재배치하는 git 작업.
|
|
2
|
+
|
|
3
|
+
`provision` 의 재진입 경로와 `stage_rebase` 의 의존 순서 재배치가 함께 쓴다.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import tempfile
|
|
8
|
+
from collections.abc import Iterator
|
|
9
|
+
from contextlib import contextmanager
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from .cleanliness import is_dirty_excluding_okstra
|
|
13
|
+
from .git_ops import _git, is_ancestor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _checked_git(root: Path, *args: str) -> str:
|
|
17
|
+
result = _git(root, *args)
|
|
18
|
+
if result.returncode:
|
|
19
|
+
raise RuntimeError(f"git {' '.join(args)} failed: {result.stderr.strip()}")
|
|
20
|
+
return result.stdout.strip()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@contextmanager
|
|
24
|
+
def _stage_worktree(project_root: Path, branch: str) -> Iterator[Path]:
|
|
25
|
+
worktrees = _checked_git(project_root, "worktree", "list", "--porcelain")
|
|
26
|
+
holder = None
|
|
27
|
+
current = None
|
|
28
|
+
for line in worktrees.splitlines():
|
|
29
|
+
if line.startswith("worktree "):
|
|
30
|
+
current = Path(line.removeprefix("worktree "))
|
|
31
|
+
elif line == f"branch refs/heads/{branch}":
|
|
32
|
+
holder = current
|
|
33
|
+
if holder is not None and is_dirty_excluding_okstra(holder):
|
|
34
|
+
raise RuntimeError(
|
|
35
|
+
f"cannot rebase {branch}: worktree {holder} has uncommitted changes"
|
|
36
|
+
)
|
|
37
|
+
temporary = holder is None
|
|
38
|
+
if temporary:
|
|
39
|
+
holder = Path(tempfile.mkdtemp(prefix="okstra-rebase-"))
|
|
40
|
+
holder.rmdir()
|
|
41
|
+
_checked_git(project_root, "worktree", "add", str(holder), branch)
|
|
42
|
+
try:
|
|
43
|
+
yield holder
|
|
44
|
+
finally:
|
|
45
|
+
if temporary:
|
|
46
|
+
# 복구에 실패한 작업 트리는 삭제하지 않는다.
|
|
47
|
+
states = ["rebase-merge", "rebase-apply"]
|
|
48
|
+
if not any(
|
|
49
|
+
Path(_checked_git(holder, "rev-parse", "--git-path", state)).exists()
|
|
50
|
+
for state in states
|
|
51
|
+
):
|
|
52
|
+
_checked_git(project_root, "worktree", "remove", str(holder))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def rebase_stage_branch(
|
|
56
|
+
project_root: Path,
|
|
57
|
+
branch: str,
|
|
58
|
+
old_base: str,
|
|
59
|
+
new_base: str,
|
|
60
|
+
) -> str:
|
|
61
|
+
old = _checked_git(project_root, "rev-parse", f"{old_base}^{{commit}}")
|
|
62
|
+
new = _checked_git(project_root, "rev-parse", f"{new_base}^{{commit}}")
|
|
63
|
+
head = _checked_git(project_root, "rev-parse", f"refs/heads/{branch}")
|
|
64
|
+
if old == new:
|
|
65
|
+
return head
|
|
66
|
+
if not is_ancestor(project_root, old, head):
|
|
67
|
+
raise RuntimeError(
|
|
68
|
+
f"cannot rebase {branch}: recorded base {old} is not an ancestor"
|
|
69
|
+
)
|
|
70
|
+
with _stage_worktree(project_root, branch) as holder:
|
|
71
|
+
for state in ["rebase-merge", "rebase-apply", "MERGE_HEAD", "CHERRY_PICK_HEAD"]:
|
|
72
|
+
path = Path(_checked_git(holder, "rev-parse", "--git-path", state))
|
|
73
|
+
if path.exists():
|
|
74
|
+
raise RuntimeError(
|
|
75
|
+
f"cannot rebase {branch}: unfinished git operation at {path}"
|
|
76
|
+
)
|
|
77
|
+
_checked_git(
|
|
78
|
+
project_root,
|
|
79
|
+
"update-ref",
|
|
80
|
+
f"refs/okstra/rebase-backups/{branch}/{head}",
|
|
81
|
+
head,
|
|
82
|
+
)
|
|
83
|
+
return _apply_rebase(holder, branch, old, new)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _apply_rebase(holder: Path, branch: str, old: str, new: str) -> str:
|
|
87
|
+
result = _git(
|
|
88
|
+
holder,
|
|
89
|
+
"rebase",
|
|
90
|
+
"--no-autostash",
|
|
91
|
+
"--no-autosquash",
|
|
92
|
+
"--no-update-refs",
|
|
93
|
+
"--no-rebase-merges",
|
|
94
|
+
"--onto",
|
|
95
|
+
new,
|
|
96
|
+
old,
|
|
97
|
+
)
|
|
98
|
+
if result.returncode:
|
|
99
|
+
abort = _git(holder, "rebase", "--abort")
|
|
100
|
+
detail = (result.stderr or result.stdout).strip()
|
|
101
|
+
if abort.returncode:
|
|
102
|
+
raise RuntimeError(
|
|
103
|
+
f"rebase failed for {branch}; recovery worktree: {holder}; {detail}; abort: {abort.stderr}"
|
|
104
|
+
)
|
|
105
|
+
raise RuntimeError(
|
|
106
|
+
f"rebase failed for {branch}; original branch restored: {detail}"
|
|
107
|
+
)
|
|
108
|
+
return _checked_git(holder, "rev-parse", "HEAD")
|
|
@@ -310,6 +310,20 @@ def get_stage_row(
|
|
|
310
310
|
return data["tasks"].get(key)
|
|
311
311
|
|
|
312
312
|
|
|
313
|
+
def update_stage_base(
|
|
314
|
+
project_id: str, task_group: str, task_id: str, stage: int, *,
|
|
315
|
+
expected_base: str, base_ref: str,
|
|
316
|
+
) -> None:
|
|
317
|
+
key = task_key(project_id, task_group, task_id, stage)
|
|
318
|
+
with _registry_lock():
|
|
319
|
+
data = _load()
|
|
320
|
+
row = data["tasks"].get(key)
|
|
321
|
+
if row is None or row.get("base_ref") != expected_base:
|
|
322
|
+
raise RuntimeError(f"stage base changed concurrently: {key}")
|
|
323
|
+
row["base_ref"] = base_ref
|
|
324
|
+
_save(data)
|
|
325
|
+
|
|
326
|
+
|
|
313
327
|
def list_active_stage_numbers(
|
|
314
328
|
project_id: str, task_group: str, task_id: str,
|
|
315
329
|
) -> set:
|
|
@@ -10,7 +10,7 @@ Output: `{ok: true, outcome: {renderArgv: ["--lead-runtime", "...", ...], render
|
|
|
10
10
|
|
|
11
11
|
`renderArgv` is the canonical ordered `okstra render-bundle` argument list. Pass its tokens verbatim and in order. Repeated `--role-count` and `--role-model` flags are intentional. `renderArgs` is a compatibility and inspection view only; do not reconstruct an invocation from it. `orchestration` holds signals this skill acts on itself — never pass its entries to `render-bundle`.
|
|
12
12
|
|
|
13
|
-
`userAuthorization`, when present, preserves the confirmation prompt actually emitted by this wizard and the relayed `proceed` response. Its `--user-authorization-json` token carries that record into the run manifest; preserve it unchanged. Do not synthesize a receipt for older confirmed states.
|
|
13
|
+
`userAuthorization`, when present, preserves the confirmation prompt actually emitted by this wizard and the relayed `proceed` response. Its `--user-authorization-json` token carries that record into the run manifest; preserve it unchanged. Do not synthesize a receipt for older confirmed states. The record documents task scope; it does not grant host execution privileges.
|
|
14
14
|
|
|
15
15
|
Before retrying a worker in an existing run, inspect its last invocation attempt: await running work; append an attempt only after `failed-no-mutation` within the retry budget. Preserve every other terminal attempt. `okstra team dispatch` routes the legacy declaration-only implementer failure to a separate same-model evidence-recovery invocation; collect its result before independent verification. Other terminal failures need `okstra agent-prompt materialize` with a new `--invocation-id`, distinct `--prompt`/`--result` paths, and unchanged assignment/model for a bounded correction. Do not restart completed implementation or request renewed approval for unchanged scope. Enforcement: `dispatch_core._next_attempt` and `execution_manifest._validate_next_attempt`.
|
|
16
16
|
|
|
@@ -167,7 +167,7 @@ Axis scope — each reviewer works all four axes; the `Reads` column names each
|
|
|
167
167
|
| `structural` | non-test source file × `structural` | core principles 1, 2, 3, 6; `clean-code.md` DRY / KISS / SOLID / YAGNI; the completion sweep's domain-literal and documented-fork items; plus the architecture pack when one was routed |
|
|
168
168
|
| `semantic` | function × `semantic` | core principles 4, 5; `clean-code.md` meaningful naming, functions-do-one-thing, the plain-English summary test, the 50-line cap, nesting depth, magic numbers, comments-explain-why |
|
|
169
169
|
| `state-and-tests` | non-test source file × `state-and-tests`, test file × `state-and-tests` | `clean-code.md` "Mutation and state boundaries" and "Testing discipline"; plus the routed language pack's self-mock signals |
|
|
170
|
-
| `general` | non-test source file × `general` | correctness bugs, swallowed errors, security, clear performance problems, core principle 7 (fix at the cause — nothing previously green edited to silence a failure); `clean-code.md` "Wrapping a third-party call" — a recovery branch the library already performs, a comment naming a condition the call site never establishes, a rethrow that drops the original error; plus the routed language pack's language-specific traps |
|
|
170
|
+
| `general` | non-test source file × `general` | correctness bugs, swallowed errors, `clean-code.md` "Untrusted input and security" (work that grows faster than the input before a size bound rejects it, input building SQL / shell / paths / fetch targets / templates / regexes, a new entry point without auth or ownership checks, secrets or personal data in code, logs or responses, evaluating input), clear performance problems, core principle 7 (fix at the cause — nothing previously green edited to silence a failure); `clean-code.md` "Wrapping a third-party call" — a recovery branch the library already performs, a comment naming a condition the call site never establishes, a rethrow that drops the original error; plus the routed language pack's language-specific traps |
|
|
171
171
|
|
|
172
172
|
Say it plainly in every brief: **the census is law** — work the cells exactly as given, return a verdict for every one, never re-derive the list. There is no length budget; dropping a finding to stay brief is the failure this skill exists to prevent.
|
|
173
173
|
|
|
@@ -223,7 +223,7 @@ Output: `{ok: true, outcome: {renderArgv: ["--lead-runtime", "...", ...], render
|
|
|
223
223
|
|
|
224
224
|
`renderArgv` is the canonical ordered `okstra render-bundle` argument list. Pass its tokens verbatim and in order. Repeated `--role-count` and `--role-model` flags are intentional. `renderArgs` is a compatibility and inspection view only; do not reconstruct an invocation from it. `orchestration` holds signals this skill acts on itself — never pass its entries to `render-bundle`.
|
|
225
225
|
|
|
226
|
-
`userAuthorization`, when present, preserves the confirmation prompt actually emitted by this wizard and the relayed `proceed` response. Its `--user-authorization-json` token carries that record into the run manifest; preserve it unchanged. Do not synthesize a receipt for older confirmed states.
|
|
226
|
+
`userAuthorization`, when present, preserves the confirmation prompt actually emitted by this wizard and the relayed `proceed` response. Its `--user-authorization-json` token carries that record into the run manifest; preserve it unchanged. Do not synthesize a receipt for older confirmed states. The record documents task scope; it does not grant host execution privileges.
|
|
227
227
|
|
|
228
228
|
Before retrying a worker in an existing run, inspect its last invocation attempt: await running work; append an attempt only after `failed-no-mutation` within the retry budget. Preserve every other terminal attempt. `okstra team dispatch` routes the legacy declaration-only implementer failure to a separate same-model evidence-recovery invocation; collect its result before independent verification. Other terminal failures need `okstra agent-prompt materialize` with a new `--invocation-id`, distinct `--prompt`/`--result` paths, and unchanged assignment/model for a bounded correction. Do not restart completed implementation or request renewed approval for unchanged scope. Enforcement: `dispatch_core._next_attempt` and `execution_manifest._validate_next_attempt`.
|
|
229
229
|
|
|
@@ -95,6 +95,15 @@ carry. release-handoff drafts its PR bodies from the same rules.
|
|
|
95
95
|
|
|
96
96
|
### A4. Output and offer to create the PR
|
|
97
97
|
|
|
98
|
+
Write the title and body to a temp file and check them:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
okstra pr check --body-file <path-to-body>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Each printed `L<line>: <pattern>: <match>` row is an okstra identifier a reviewer cannot open:
|
|
105
|
+
restate that line in the reviewer's terms and check again. `clean` means none is left.
|
|
106
|
+
|
|
98
107
|
Print the filled PR body to chat as a single fenced markdown block. Then ask the
|
|
99
108
|
user whether to open a real PR. Only on an explicit yes:
|
|
100
109
|
|
|
@@ -256,7 +256,7 @@ Output: `{ok: true, outcome: {renderArgv: ["--lead-runtime", "...", ...], render
|
|
|
256
256
|
|
|
257
257
|
`renderArgv` is the canonical ordered `okstra render-bundle` argument list. Pass its tokens verbatim and in order. Repeated `--role-count` and `--role-model` flags are intentional. `renderArgs` is a compatibility and inspection view only; do not reconstruct an invocation from it. `orchestration` holds signals this skill acts on itself — never pass its entries to `render-bundle`.
|
|
258
258
|
|
|
259
|
-
`userAuthorization`, when present, preserves the confirmation prompt actually emitted by this wizard and the relayed `proceed` response. Its `--user-authorization-json` token carries that record into the run manifest; preserve it unchanged. Do not synthesize a receipt for older confirmed states.
|
|
259
|
+
`userAuthorization`, when present, preserves the confirmation prompt actually emitted by this wizard and the relayed `proceed` response. Its `--user-authorization-json` token carries that record into the run manifest; preserve it unchanged. Do not synthesize a receipt for older confirmed states. The record documents task scope; it does not grant host execution privileges.
|
|
260
260
|
|
|
261
261
|
Before retrying a worker in an existing run, inspect its last invocation attempt: await running work; append an attempt only after `failed-no-mutation` within the retry budget. Preserve every other terminal attempt. `okstra team dispatch` routes the legacy declaration-only implementer failure to a separate same-model evidence-recovery invocation; collect its result before independent verification. Other terminal failures need `okstra agent-prompt materialize` with a new `--invocation-id`, distinct `--prompt`/`--result` paths, and unchanged assignment/model for a bounded correction. Do not restart completed implementation or request renewed approval for unchanged scope. Enforcement: `dispatch_core._next_attempt` and `execution_manifest._validate_next_attempt`.
|
|
262
262
|
|
|
@@ -169,8 +169,8 @@ matching section:
|
|
|
169
169
|
- **D. PR body template** (release-handoff) — opt-in pre-registration; also
|
|
170
170
|
offered on the first release-handoff run.
|
|
171
171
|
- **E. final report language** (`reportLanguage`) — a language tag (`en`,
|
|
172
|
-
`ko`, `fr`, …); default `en`. The
|
|
173
|
-
|
|
172
|
+
`ko`, `fr`, …); default `en`. The report writer writes the report prose
|
|
173
|
+
directly in it.
|
|
174
174
|
- **F. declared architecture style** (`architecture.style`) — `hexagonal` /
|
|
175
175
|
`layered` / `none`; hand-added, default `none`. Declaring one makes that
|
|
176
176
|
architecture's placement rules binding in planning + verification.
|
|
@@ -196,8 +196,8 @@ per-run override prompt during the next release-handoff run.
|
|
|
196
196
|
|
|
197
197
|
## E. Final report language (`reportLanguage`)
|
|
198
198
|
|
|
199
|
-
The value is a language tag (`en`, `ko`, `fr`, `pt-BR`, …). The
|
|
200
|
-
|
|
199
|
+
The value is a language tag (`en`, `ko`, `fr`, `pt-BR`, …). The report writer
|
|
200
|
+
writes the report prose directly in that language; no translation step runs.
|
|
201
201
|
If you skip, no `reportLanguage` field is written and the runtime uses `en`.
|
|
202
202
|
|
|
203
203
|
AskUserQuestion (fixed options):
|
|
@@ -30,3 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
When the only source for a claim is such an artifact, restate its substance in the reviewer's
|
|
32
32
|
terms — what changed in the code, and why — instead of citing the id.
|
|
33
|
+
|
|
34
|
+
Check the drafted title and body with `okstra pr check --body-file <file>` from the repository:
|
|
35
|
+
it prints each line that still carries one of these identifiers, skipping terms the
|
|
36
|
+
repository's own tracked files use.
|
|
@@ -30,7 +30,7 @@ The narrative is not free-form Markdown. A narrative file is the line `# OKSTRA
|
|
|
30
30
|
|
|
31
31
|
Blank lines are ignored. **Every other line is rejected**, including Markdown headings (`#`, `##`, `###`), a pipe table written at column 0, code fences, bare paragraphs, JSON, YAML, and JSON Pointer. A heading or table that belongs in the reader's report is written as the text of a `> ` value, never as a narrative line of its own.
|
|
32
32
|
|
|
33
|
-
Follow the task-specific schema order
|
|
33
|
+
Follow the task-specific schema order. Write every `> ` prose value directly in the language named by `**Report Language:**`; do not draft in English first, because no translation step follows. Field names, `Verdict Token` and other enum or status values, finding and requirement ids, paths, symbols, commands, and model names stay exactly as the schema and sources spell them.
|
|
34
34
|
|
|
35
35
|
### Top-level fields you may author
|
|
36
36
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This file is the audience-specific contract for initial `analysis` workers. Read it end-to-end from the path selected by the dispatch prompt. Before doing work, also read the shared file named by `**Worker Error Contract Path:**`; that file is the only source for error-sidecar rules.
|
|
4
4
|
|
|
5
|
-
Your identity is the stored role execution (`executionLabel`). Canonical roles are `leader`, `analyser`, `critic`, `designer`, `planner`, `implementer`, `verifier`, `report-writer
|
|
5
|
+
Your identity is the stored role execution (`executionLabel`). Canonical roles are `leader`, `analyser`, `critic`, `designer`, `planner`, `implementer`, `verifier`, and `report-writer`. `lead` and `executor` are compatibility aliases only.
|
|
6
6
|
|
|
7
7
|
## Required reading
|
|
8
8
|
|
|
@@ -230,7 +230,6 @@ from okstra_ctl.workflow import ( # noqa: E402
|
|
|
230
230
|
)
|
|
231
231
|
from okstra_ctl.final_report_paths import ( # noqa: E402
|
|
232
232
|
final_report_data_path as _data_path_for,
|
|
233
|
-
translation_sidecar_path,
|
|
234
233
|
)
|
|
235
234
|
from okstra_token_usage.report import _match_worker_index # noqa: E402
|
|
236
235
|
from okstra_ctl.phases.requirements_discovery.validation import (
|
|
@@ -3328,7 +3327,7 @@ _CITATION_LIST_KEYS = ("evidenceRefs", "supportingEvidence", "falsifyingEvidence
|
|
|
3328
3327
|
|
|
3329
3328
|
def _prose_pointers(node: object, key: str = "", pointer: str = "") -> list[tuple[str, str]]:
|
|
3330
3329
|
"""(JSON pointer, value) for every string under a reader-facing prose key."""
|
|
3331
|
-
from okstra_ctl.
|
|
3330
|
+
from okstra_ctl.report_prose_keys import PROSE_KEYS
|
|
3332
3331
|
|
|
3333
3332
|
out: list[tuple[str, str]] = []
|
|
3334
3333
|
if isinstance(node, dict):
|
|
@@ -4685,29 +4684,6 @@ def _validate_improvement_discovery(
|
|
|
4685
4684
|
failures.append(f"improvement-discovery: {err}")
|
|
4686
4685
|
|
|
4687
4686
|
|
|
4688
|
-
def _validate_translation_sidecar(
|
|
4689
|
-
data: Mapping[str, Any], report_path: Path, failures: list[str]
|
|
4690
|
-
) -> None:
|
|
4691
|
-
"""영어가 아닌 사람 HTML 은 번역 사이드카가 있어야 한다.
|
|
4692
|
-
|
|
4693
|
-
사이드카가 없으면 렌더러가 크롬만 번역하고 본문은 영어로 남긴다.
|
|
4694
|
-
`render-views` 가 그 조합을 거부하고, 여기가 같은 규칙의 두 번째 게이트다.
|
|
4695
|
-
"""
|
|
4696
|
-
if not data:
|
|
4697
|
-
return
|
|
4698
|
-
lang = str((data.get("meta") or {}).get("reportLanguage") or "en")
|
|
4699
|
-
if lang == "en":
|
|
4700
|
-
return
|
|
4701
|
-
sidecar = translation_sidecar_path(report_path, lang)
|
|
4702
|
-
if not sidecar.is_file():
|
|
4703
|
-
failures.append(
|
|
4704
|
-
f"final-report has reportLanguage {lang!r} but no translation "
|
|
4705
|
-
f"sidecar at {sidecar.name}. The human HTML rendered from the "
|
|
4706
|
-
"English source instead; re-run `report-finalize --only translate "
|
|
4707
|
-
"--only render-views` to dispatch the translator and overlay it."
|
|
4708
|
-
)
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
4687
|
def _validate_session_conformance(
|
|
4712
4688
|
team_state: dict,
|
|
4713
4689
|
team_state_path: Path,
|
|
@@ -6003,9 +5979,6 @@ def main() -> int:
|
|
|
6003
5979
|
),
|
|
6004
5980
|
)
|
|
6005
5981
|
validation_data = report_data if isinstance(report_data, Mapping) else {}
|
|
6006
|
-
_validate_translation_sidecar(
|
|
6007
|
-
validation_data, _data_path_for(report_path), failures
|
|
6008
|
-
)
|
|
6009
5982
|
validate_report(
|
|
6010
5983
|
report_path,
|
|
6011
5984
|
failures,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": "1.0",
|
|
3
|
-
"id": "translator",
|
|
4
|
-
"legacyAliases": [],
|
|
5
|
-
"identity": "Renders the report's reader-facing text in another language without editing the underlying decision.",
|
|
6
|
-
"responsibilities": [
|
|
7
|
-
"Carry the source's precision, uncertainty, and operational force into the target language.",
|
|
8
|
-
"Preserve identifiers, paths, commands, and status tokens exactly as the source writes them."
|
|
9
|
-
],
|
|
10
|
-
"requiredCapabilities": [
|
|
11
|
-
"extended-artifact-authoring",
|
|
12
|
-
"source-readonly",
|
|
13
|
-
"worker-artifact-io"
|
|
14
|
-
],
|
|
15
|
-
"prohibitions": [
|
|
16
|
-
"Do not edit the source, translate an unassigned file, or add analysis of your own.",
|
|
17
|
-
"Do not strengthen or weaken a normative statement in translation."
|
|
18
|
-
]
|
|
19
|
-
}
|