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.
Files changed (92) hide show
  1. package/README.md +5 -5
  2. package/dist/cli-registry.mjs +0 -10
  3. package/dist/cli-registry.mjs.map +1 -1
  4. package/dist/commands/lifecycle/config.mjs +2 -3
  5. package/dist/commands/lifecycle/config.mjs.map +1 -1
  6. package/dist/commands/pr/pr.d.mts +6 -0
  7. package/dist/commands/pr/pr.mjs +58 -0
  8. package/dist/commands/pr/pr.mjs.map +1 -1
  9. package/dist/lib/runtime-payload.mjs +1 -2
  10. package/dist/lib/runtime-payload.mjs.map +1 -1
  11. package/docs/architecture/storage-model.md +1 -1
  12. package/docs/architecture.md +9 -7
  13. package/docs/cli.md +3 -4
  14. package/docs/project-structure-overview.md +4 -5
  15. package/package.json +1 -1
  16. package/runtime/BUILD.json +2 -2
  17. package/runtime/prompts/launch.template.md +1 -1
  18. package/runtime/prompts/lead/okstra-lead-contract.md +1 -3
  19. package/runtime/prompts/lead/report-writer.md +10 -17
  20. package/runtime/prompts/lead/team-contract.md +1 -2
  21. package/runtime/prompts/wizard/prompts.ko.json +0 -1
  22. package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +1 -1
  23. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +1 -1
  24. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +0 -1
  25. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +1 -1
  26. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +0 -1
  27. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +0 -1
  28. package/runtime/python/okstra_ctl/agent/invocation.py +1 -2
  29. package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +2 -2
  30. package/runtime/python/okstra_ctl/agent/prompt_cli/jobs.py +0 -2
  31. package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +2 -27
  32. package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +0 -4
  33. package/runtime/python/okstra_ctl/dispatch_core.py +3 -349
  34. package/runtime/python/okstra_ctl/dispatch_state.py +0 -13
  35. package/runtime/python/okstra_ctl/domain/provider.py +1 -1
  36. package/runtime/python/okstra_ctl/domain/role.py +0 -6
  37. package/runtime/python/okstra_ctl/final_report_paths.py +0 -16
  38. package/runtime/python/okstra_ctl/i18n.py +2 -2
  39. package/runtime/python/okstra_ctl/path_hints.py +0 -4
  40. package/runtime/python/okstra_ctl/paths.py +0 -3
  41. package/runtime/python/okstra_ctl/phases/final_verification/profile.md +2 -0
  42. package/runtime/python/okstra_ctl/phases/final_verification/spec.md +2 -0
  43. package/runtime/python/okstra_ctl/phases/final_verification/target.py +17 -1
  44. package/runtime/python/okstra_ctl/phases/implementation/entry.py +12 -0
  45. package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-deliverable.md +1 -0
  46. package/runtime/python/okstra_ctl/phases/implementation/instructions/_implementation-executor.md +1 -0
  47. package/runtime/python/okstra_ctl/phases/implementation/spec.md +4 -0
  48. package/runtime/python/okstra_ctl/phases/release_handoff/boundary.json +1 -1
  49. package/runtime/python/okstra_ctl/phases/release_handoff/operations.py +24 -3
  50. package/runtime/python/okstra_ctl/phases/release_handoff/profile.md +11 -11
  51. package/runtime/python/okstra_ctl/phases/release_handoff/spec.md +4 -2
  52. package/runtime/python/okstra_ctl/render.py +0 -10
  53. package/runtime/python/okstra_ctl/render_final_report.py +4 -4
  54. package/runtime/python/okstra_ctl/report_finalize.py +37 -76
  55. package/runtime/python/okstra_ctl/report_html/render.py +4 -44
  56. package/runtime/python/okstra_ctl/report_language.py +3 -3
  57. package/runtime/python/okstra_ctl/report_prose_keys.py +153 -0
  58. package/runtime/python/okstra_ctl/report_synthesis_packet.py +1 -1
  59. package/runtime/python/okstra_ctl/run.py +1 -21
  60. package/runtime/python/okstra_ctl/stage_integrate.py +37 -2
  61. package/runtime/python/okstra_ctl/stage_rebase.py +189 -0
  62. package/runtime/python/okstra_ctl/stage_targets.py +29 -62
  63. package/runtime/python/okstra_ctl/team.py +1 -6
  64. package/runtime/python/okstra_ctl/wizard/confirmation.py +0 -8
  65. package/runtime/python/okstra_ctl/wizard/registry.py +2 -3
  66. package/runtime/python/okstra_ctl/wizard/sources.py +10 -0
  67. package/runtime/python/okstra_ctl/worker_prompt_policy.py +3 -12
  68. package/runtime/python/okstra_ctl/worktree/__init__.py +1 -1
  69. package/runtime/python/okstra_ctl/worktree/decisions.py +3 -4
  70. package/runtime/python/okstra_ctl/worktree/provision.py +27 -0
  71. package/runtime/python/okstra_ctl/worktree/rebase.py +108 -0
  72. package/runtime/python/okstra_ctl/worktree_registry.py +14 -0
  73. package/runtime/skills/_fragments/wizard-render-bundle.md +1 -1
  74. package/runtime/skills/okstra-code-review/SKILL.md +1 -1
  75. package/runtime/skills/okstra-discussion/SKILL.md +1 -1
  76. package/runtime/skills/okstra-pr-gen/SKILL.md +9 -0
  77. package/runtime/skills/okstra-run/SKILL.md +1 -1
  78. package/runtime/skills/okstra-setup/SKILL.md +2 -2
  79. package/runtime/skills/okstra-setup/references/project-config.md +2 -2
  80. package/runtime/templates/pr/drafting-rules.md +4 -0
  81. package/runtime/templates/report-writer-prompt-preamble.md +1 -1
  82. package/runtime/templates/reports/html/i18n/en.json +0 -1
  83. package/runtime/templates/reports/html/i18n/ko.json +0 -1
  84. package/runtime/templates/worker-prompt-preamble.md +1 -1
  85. package/runtime/validators/validate-run.py +1 -28
  86. package/runtime/agents/operations/report-translation.json +0 -6
  87. package/runtime/agents/roles/translator.json +0 -19
  88. package/runtime/bin/okstra-report-translate.py +0 -404
  89. package/runtime/prompts/duties/translator.json +0 -32
  90. package/runtime/python/okstra_ctl/report_translation.py +0 -509
  91. package/runtime/python/okstra_ctl/report_translation_dispatch.py +0 -356
  92. package/runtime/templates/translator-prompt-preamble.md +0 -36
@@ -1,356 +0,0 @@
1
- """Phase 7 `translate` 단계 — 번역 사이드카를 okstra 가 직접 만든다.
2
-
3
- 비영어 리포트의 번역 워커는 종전에 리드의 수동 절차였다: `agent-prompt
4
- materialize --audience translator` 로 예약을 만들고, `worker-dispatch --workers
5
- translator` 로 띄우고, `report-finalize` 를 두 번에 나눠 도는 순서가 리드가
6
- lazy-read 하는 문서(`prompts/lead/report-writer.md`)에만 있었다. 실측(2026-09-09,
7
- fontsninja-v3-site dev-10628-3 implementation-option-selection, grok 리드): 리드가
8
- `report-finalize` 를 한 번에 돌려 번역 예약이 0건이었고, 디스패처는 0건을 거절할
9
- 뿐 만들지 못한다(`dispatch_core._translator_job_from_reservation`). 검증은 권고
10
- 한 줄을 남겼고 HTML 은 영어로 남았다.
11
-
12
- 이 모듈은 그 절차를 `report-finalize` 의 한 단계로 내린다. 예약이 없으면 지시문과
13
- 프롬프트를 발행하고(`agent-prompt materialize` 와 같은 코드), 워커를 띄운 뒤
14
- 사이드카가 생겼는지 본다. cmux run 이면 다른 워커와 같은 pane 으로 띄우고 끝날
15
- 때까지 기다린다(`team dispatch` + `team await` 와 같은 코드). 그 밖의 run 은 CLI
16
- 래퍼 디스패처로 띄운다. 디스패치 기록과 결과 링크는 `dispatch_core` 가 배치 안에서
17
- 이미 하므로 여기서 다시 하지 않는다.
18
- """
19
- from __future__ import annotations
20
-
21
- import contextlib
22
- import io
23
- import json
24
- from dataclasses import dataclass
25
- from pathlib import Path
26
- from typing import Any, Mapping
27
-
28
- from . import worker_dispatch
29
- from .agent.prompt_cli.cli import main as agent_prompt_main
30
- from .application.dispatch_assignments import dispatch_assignments
31
- from .dispatch_core import (
32
- BACKEND_CMUX_PANE,
33
- await_dispatches,
34
- dispatch_plan,
35
- translator_reservations,
36
- )
37
- from .ports.worker_dispatch import WorkerDispatchRequest
38
- from .team import team_dispatch_port
39
- from .dispatch_state import (
40
- DispatchError,
41
- load_json_object,
42
- resolve_project_path,
43
- resolve_required_path,
44
- )
45
- from .final_report_paths import translation_sidecar_path
46
- from .json_boundary import JsonBoundaryError
47
-
48
- TRANSLATOR_WORKER_ID = "translator"
49
-
50
- # 리드가 종전에 손으로 쓰던 지시문(실측 2026-09-08, fontsninja-nlpvibe dev-10786
51
- # requirements-discovery-002)과 같은 절차다. 명령은 `agents/workers/translator-worker.md`
52
- # 의 Procedure 와 같은 세 개이고, 결과·감사 경로는 프롬프트 앵커가 이름한다.
53
- _INSTRUCTIONS = """## Instructions
54
-
55
- Translate the finalized report into the language whose IETF language tag is `{language}` faithfully. This is translation only: no analysis, no source edits.
56
-
57
- 1. Run `okstra report-translate source --run-manifest {manifest}` and read the complete work list, in chunks if it is long. Copy the `Source digest` it prints.
58
- 2. Write one translated block per `## T-NNN` item to the exact Result Path, using the same `## T-NNN` headings. Preserve every identifier, path, command, enum token, link, and qualification character for character.
59
- 3. Publish with `okstra report-translate write --run-manifest {manifest} --source-digest <digest from step 1> --translations <Result Path>`, then run `okstra report-translate check-data --run-manifest {manifest}`. Fix the translation until that check passes; do not return on a failing check.
60
- 4. Write a completion pointer to the Worker Result Path naming the translations file, the published sidecar, the digest, and the check result. Write the audit sidecar with reading, completeness, and terminology checks.
61
-
62
- Do not edit the canonical report, the narrative, the HTML, or any source file. All `.okstra` paths resolve from Project Root.
63
- """
64
-
65
-
66
- @dataclass(frozen=True)
67
- class TranslateOutcome:
68
- """`translate` 단계의 결과. `report_finalize` 가 CompletedProcess 로 옮긴다."""
69
-
70
- returncode: int
71
- stdout: str = ""
72
- stderr: str = ""
73
-
74
-
75
- class TranslateError(Exception):
76
- """번역 워커를 예약하거나 띄우지 못했다. 메시지가 그 자리를 이름한다."""
77
-
78
-
79
- def report_language(manifest: Mapping[str, Any], data_path: Path) -> str:
80
- """이 run 의 사람 리포트 언어. 매니페스트가 정본이고, 없으면 리포트의 meta.
81
-
82
- 매니페스트 `reportLanguage` 는 prepare 가 `report_language.resolve_report_language`
83
- 로 정한 값이고, 조립이 `meta.reportLanguage` 로 복사한다. 그 필드가 없는 옛
84
- run 은 리포트에서 읽는다. 둘 다 없으면 영어다.
85
- """
86
- value = str(manifest.get("reportLanguage") or "").strip()
87
- if value:
88
- return value
89
- if data_path.is_file():
90
- try:
91
- data = load_json_object(data_path, "final-report data")
92
- except (DispatchError, JsonBoundaryError):
93
- return "en"
94
- meta = data.get("meta")
95
- if isinstance(meta, Mapping):
96
- value = str(meta.get("reportLanguage") or "").strip()
97
- return value or "en"
98
-
99
-
100
- def translate_report(
101
- *,
102
- project_root: Path,
103
- workspace_root: Path,
104
- manifest_path: Path,
105
- manifest: Mapping[str, Any],
106
- data_path: Path,
107
- ) -> TranslateOutcome:
108
- """비영어 리포트의 번역 사이드카를 있게 만든다. 영어 리포트는 할 일이 없다.
109
-
110
- 사이드카가 이미 있으면(앞선 호출, 또는 리드가 손으로 띄운 워커) 그대로 둔다.
111
- 없으면 예약을 보장하고 워커를 띄운 뒤, 사이드카의 존재로만 성공을 판정한다 —
112
- 워커의 종료 코드는 산출물의 존재를 대신하지 못한다.
113
- """
114
- lang = report_language(manifest, data_path)
115
- if lang == "en":
116
- return TranslateOutcome(0, f"skipped: reportLanguage is {lang!r}")
117
- sidecar = translation_sidecar_path(data_path, lang)
118
- if sidecar.is_file():
119
- return TranslateOutcome(0, f"translation sidecar already present: {sidecar.name}")
120
- try:
121
- metadata_path = ensure_translator_reservation(
122
- project_root, manifest_path, manifest, lang
123
- )
124
- dispatch = dispatch_translator(
125
- project_root, workspace_root, manifest_path, manifest
126
- )
127
- except TranslateError as exc:
128
- return TranslateOutcome(1, "", str(exc))
129
- if sidecar.is_file():
130
- return TranslateOutcome(
131
- 0,
132
- f"translated into {lang!r}: {sidecar.name} (reservation "
133
- f"{metadata_path.name})",
134
- dispatch.stderr,
135
- )
136
- return TranslateOutcome(
137
- 1,
138
- dispatch.stdout,
139
- f"translator dispatch exited {dispatch.returncode} and left no "
140
- f"translation sidecar at {sidecar}"
141
- + (f": {dispatch.stderr.strip()}" if dispatch.stderr.strip() else ""),
142
- )
143
-
144
-
145
- def ensure_translator_reservation(
146
- project_root: Path,
147
- manifest_path: Path,
148
- manifest: Mapping[str, Any],
149
- lang: str,
150
- ) -> Path:
151
- """이 run 의 아직 안 띄운 translator 예약 하나를 돌려준다. 없으면 만든다.
152
-
153
- Returns the invocation metadata path of that reservation.
154
-
155
- 예약이 둘이면 디스패처가 고를 수 없고 회수 명령도 없으므로 그대로 거절한다 —
156
- 그 상태는 리드가 다른 id 로 두 번 발행했을 때만 생긴다. 예약 id 는
157
- `<task-type>-<seq>-translator` 이고, 같은 run 의 앞선 시도가 실패해 그 id 가
158
- 이미 쓰였으면 `-r2`, `-r3` 로 잇는다.
159
- """
160
- team_state = load_json_object(
161
- resolve_required_path(project_root, manifest, "teamStatePath"), "team-state"
162
- )
163
- try:
164
- candidates, seen = translator_reservations(project_root, manifest, team_state)
165
- except DispatchError as exc:
166
- raise TranslateError(f"translator reservation lookup failed: {exc}") from exc
167
- if len(candidates) == 1:
168
- return resolve_project_path(
169
- project_root, str(candidates[0].get("metadataPath") or "")
170
- )
171
- if len(candidates) > 1:
172
- raise TranslateError(
173
- "translator dispatch requires exactly one canonical invocation "
174
- f"reservation for this run; found {len(candidates)}. Translator "
175
- "reservations seen: " + "; ".join(seen)
176
- )
177
- return _materialize_translator(project_root, manifest_path, manifest, lang, seen)
178
-
179
-
180
- def dispatch_translator(
181
- project_root: Path,
182
- workspace_root: Path,
183
- manifest_path: Path,
184
- manifest: Mapping[str, Any],
185
- ) -> TranslateOutcome:
186
- """번역 워커를 띄우고 끝날 때까지 기다린다.
187
-
188
- cmux run 은 pane 으로 띄운다 — 사용자가 다른 워커처럼 진행을 화면에서 본다.
189
- 그 밖의 run 은 `okstra worker-dispatch --workers translator` 와 같은 코드를
190
- 프로세스 안에서 돈다. 디스패처는 결과 JSON 을 stdout 에 찍는다. Phase 7 의
191
- stdout 은 finalize 결과 JSON 하나여야 하므로 여기서 받아 단계 결과로 넘긴다.
192
- """
193
- if str(manifest.get("terminalBackend") or "") == BACKEND_CMUX_PANE:
194
- return _dispatch_translator_pane(
195
- project_root, workspace_root, manifest_path, manifest
196
- )
197
- out, err = io.StringIO(), io.StringIO()
198
- with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
199
- try:
200
- code = worker_dispatch.main([
201
- "--project-root", str(project_root),
202
- "--run-manifest", str(manifest_path),
203
- "--workspace-root", str(workspace_root),
204
- "--workers", TRANSLATOR_WORKER_ID,
205
- ])
206
- except (DispatchError, OSError) as exc:
207
- raise TranslateError(f"translator dispatch failed: {exc}") from exc
208
- return TranslateOutcome(code, out.getvalue(), err.getvalue())
209
-
210
-
211
- def _dispatch_translator_pane(
212
- project_root: Path,
213
- workspace_root: Path,
214
- manifest_path: Path,
215
- manifest: Mapping[str, Any],
216
- ) -> TranslateOutcome:
217
- """`okstra team dispatch --workers translator` 뒤 `okstra team await` 와 같다.
218
-
219
- pane 을 열지 못하면 디스패처가 CLI 래퍼로 강등하고 `DEGRADED` 줄과
220
- `degradedReason` 을 남긴다 — 그 경우도 여기서 같은 대기로 정산된다. pane 은
221
- run 끝의 `okstra team teardown` 이 기록을 보고 닫는다.
222
- """
223
- request = WorkerDispatchRequest(
224
- project_root=project_root,
225
- run_manifest=manifest_path,
226
- workspace_root=workspace_root,
227
- okstra_bin=None,
228
- requested_workers=(TRANSLATOR_WORKER_ID,),
229
- idle_timeout_seconds=None,
230
- dispatch_kind="initial",
231
- jobs_file=None,
232
- )
233
- out, err = io.StringIO(), io.StringIO()
234
- with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
235
- try:
236
- plan = dispatch_assignments(request, team_dispatch_port(manifest))
237
- backend_plan = plan.backend_plan
238
- if backend_plan is None:
239
- raise TranslateError(
240
- "translator pane dispatch did not produce a backend plan"
241
- )
242
- code = dispatch_plan(backend_plan, wait=False)
243
- if code == 0:
244
- code = await_dispatches(backend_plan, heartbeat_seconds=0)
245
- except (DispatchError, OSError) as exc:
246
- raise TranslateError(f"translator dispatch failed: {exc}") from exc
247
- return TranslateOutcome(code, out.getvalue(), err.getvalue())
248
-
249
-
250
- def _materialize_translator(
251
- project_root: Path,
252
- manifest_path: Path,
253
- manifest: Mapping[str, Any],
254
- lang: str,
255
- seen: list[str],
256
- ) -> Path:
257
- task_type = str(manifest.get("taskType") or "").strip()
258
- if not task_type:
259
- raise TranslateError("run manifest has no taskType")
260
- run_dir = resolve_required_path(project_root, manifest, "runDirectoryPath")
261
- base_id = f"{task_type}-{_seq(manifest, 'manifests')}-translator"
262
- used = {entry.split(" ", 1)[0] for entry in seen}
263
- invocation_id = base_id
264
- attempt = 1
265
- while invocation_id in used:
266
- attempt += 1
267
- invocation_id = f"{base_id}-r{attempt}"
268
- prompt_seq = _seq(manifest, "prompts")
269
- result_seq = _seq(manifest, "workerResults")
270
- suffix = "" if attempt == 1 else f"-r{attempt}"
271
- instruction = (
272
- _instruction_root(project_root, manifest, run_dir)
273
- / f"translator-instructions-{task_type}-{_seq(manifest, 'state')}{suffix}.md"
274
- )
275
- prompt = run_dir / "prompts" / f"translator-worker-prompt-{task_type}-{prompt_seq}{suffix}.md"
276
- translations = (
277
- run_dir / "worker-results"
278
- / f"translator-translations-{task_type}-{result_seq}{suffix}.md"
279
- )
280
- worker_result = (
281
- run_dir / "worker-results" / f"translator-worker-{task_type}-{result_seq}{suffix}.md"
282
- )
283
- if not instruction.is_file():
284
- # 리드가 먼저 써 둔 지시문은 그대로 쓴다. 없을 때만 okstra 의 것을 쓴다.
285
- instruction.parent.mkdir(parents=True, exist_ok=True)
286
- instruction.write_text(
287
- _INSTRUCTIONS.format(
288
- language=lang,
289
- manifest=manifest_path.relative_to(project_root).as_posix()
290
- if manifest_path.is_relative_to(project_root)
291
- else str(manifest_path),
292
- ),
293
- encoding="utf-8",
294
- )
295
- out, err = io.StringIO(), io.StringIO()
296
- with contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
297
- code = agent_prompt_main([
298
- "materialize",
299
- "--project-root", str(project_root),
300
- "--run-manifest", str(manifest_path),
301
- "--invocation-id", invocation_id,
302
- "--worker-id", TRANSLATOR_WORKER_ID,
303
- "--audience", TRANSLATOR_WORKER_ID,
304
- "--dispatch-kind", TRANSLATOR_WORKER_ID,
305
- "--assignment-ref", TRANSLATOR_WORKER_ID,
306
- "--instruction", str(instruction),
307
- "--prompt", str(prompt),
308
- "--result", str(translations),
309
- "--audit-source", str(worker_result),
310
- "--json",
311
- ])
312
- if code != 0:
313
- raise TranslateError(
314
- "translator prompt materialization failed: "
315
- + (err.getvalue().strip() or out.getvalue().strip() or f"exit {code}")
316
- )
317
- try:
318
- payload = json.loads(out.getvalue())
319
- except json.JSONDecodeError as exc:
320
- raise TranslateError(
321
- f"translator materialization printed no JSON: {out.getvalue()[:200]!r}"
322
- ) from exc
323
- metadata = str(payload.get("metadataPath") or "")
324
- if not metadata:
325
- raise TranslateError("translator materialization named no metadataPath")
326
- return resolve_project_path(project_root, metadata)
327
-
328
-
329
- def _instruction_root(
330
- project_root: Path, manifest: Mapping[str, Any], run_dir: Path,
331
- ) -> Path:
332
- """지시문을 둘 디렉터리. materialize 는 `authorizedPaths.instructionRoots` 밖의
333
- 지시문을 거절하므로 그 목록에서 고른다 — 리드가 쓰던 `state/` 가 있으면 그것,
334
- 없으면 첫 항목, 목록이 없으면 run 의 `state/`."""
335
- contract = manifest.get("agentContract")
336
- authorized = contract.get("authorizedPaths") if isinstance(contract, Mapping) else None
337
- roots = authorized.get("instructionRoots") if isinstance(authorized, Mapping) else None
338
- candidates = [
339
- resolve_project_path(project_root, str(root))
340
- for root in (roots if isinstance(roots, list) else [])
341
- if str(root).strip()
342
- ]
343
- for root in candidates:
344
- if root.name == "state":
345
- return root
346
- return candidates[0] if candidates else run_dir / "state"
347
-
348
-
349
- def _seq(manifest: Mapping[str, Any], category: str) -> str:
350
- seqs = manifest.get("runSequencesByCategory")
351
- if not isinstance(seqs, Mapping):
352
- raise TranslateError("run manifest has no runSequencesByCategory object")
353
- value = str(seqs.get(category) or seqs.get("manifests") or "").strip()
354
- if not value:
355
- raise TranslateError(f"run manifest has no {category} sequence")
356
- return value
@@ -1,36 +0,0 @@
1
- # Translator Prompt Preamble (canonical)
2
-
3
- This file is the audience-specific contract for `translator`. Read it and the shared Worker Error Contract end-to-end. The task instructions in this prompt own this run's commands, paths, and target language; this file owns how you translate and what you may write.
4
-
5
- ## Output ownership
6
-
7
- Write only the translations file at `**Result Path:**`, the completion pointer at `**Worker Result Path:**`, and the reading audit at `**Audit sidecar path:**`. The publish command named in the task instructions owns the sidecar the renderer overlays; do not write that file yourself.
8
-
9
- Never edit the report record, the narrative Markdown, the rendered HTML, or any project source file.
10
-
11
- Never add, remove, or re-order the `T-NNN` items relative to the work list you were given. Never translate a value the work list did not offer you: its absence is deliberate, because the renderer reads those values as machinery and a translated copy breaks the page silently.
12
-
13
- Never return the translated text inline. The file on disk is the artifact; your return message names the paths and the verification result.
14
-
15
- ## Required reading
16
-
17
- Read the complete work list before translating any item, continuing in chunks when it is long. A partial read produces a partial sidecar, and the items you never saw render in the source language with no error raised.
18
-
19
- ## How to translate
20
-
21
- Judge every term on whether the translation or the original carries the meaning faster to a working developer in the target language, and pick that one. The goal is a reader who understands the report sooner, not a document with no English left in it.
22
-
23
- - **Never touch**: code identifiers, file paths, CLI commands and flags, model names, commit SHAs, URLs, and anything already inside backticks. Reproduce them character for character.
24
- - **Keep the English word** when that is what developers in the target language actually say. Forcing a native coinage onto `commit`, `worktree`, `merge`, `lint`, `diff`, `stage`, `rollback` or `PR` makes the sentence *slower* to read, not more local.
25
- - **Translate the explanation.** Connective prose — why something matters, what a reader should do, what a finding means — is where the translation earns its place. Carry the meaning, not the word order.
26
- - **Do not translate literally.** A word-for-word rendering that is technically correct and unreadable has failed. Say what the sentence means the way a developer would say it.
27
- - **Gloss on first use, once.** When a technical term does need translating, write it as `<translation>(<English>)` the first time it appears in the document, then use the translation alone. Never gloss the same term twice.
28
- - **One claim per sentence.** Where the English stacks four clauses behind em-dashes, split it. The reader gains nothing from the original's punctuation.
29
- - **Match the register.** A verdict line is terse; a rationale paragraph is explanatory. Do not inflate a three-word cell into a sentence, or compress a paragraph into a fragment.
30
- - **Leave it out when you cannot do it justice.** An omitted item renders in the source language, which is a correct fallback. A confident mistranslation is not.
31
-
32
- ## Failure handling
33
-
34
- A failing verification check means a pointer resolves nowhere — you altered or invented one. Fix the translation and run the check again; do not return while it fails.
35
-
36
- Record your own tool failures through the file named by `**Worker Error Contract Path:**`.