okstra 0.92.1 → 0.94.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.
@@ -0,0 +1,476 @@
1
+ # 에이전트 작업 자세(Operating Standard) 주입 — 구현 계획
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** 전 okstra 에이전트에 공통 "작업 자세" 행동 규약을 주입하고, 코드 작성 워커(executor)에 완료 전 자기검증 게이트를 추가한다.
6
+
7
+ **Architecture:** 공통 코어를 `templates/operating-standard.md` SSOT 한 곳에 두고, 워커 preamble·리드 contract 가 그 코어를 verbatim 인라인한다(앵커 추가-read 없음 — 토큰 절약). 단위 테스트가 인라인 사본이 SSOT 와 일치하는지 검증해 drift 를 막는다. 코드 워커 완료 전 게이트는 별도 sidecar `_implementation-self-check.md` 로 신설하고, 기존 preflight 와 동일한 전달 경로(Claude executor 직접 read / codex·antigravity executor 는 lead 가 dispatch 프롬프트에 body append)를 탄다.
8
+
9
+ **Tech Stack:** Markdown 프롬프트/계약 파일, Python(`scripts/okstra_ctl/codex_dispatch.py`), pytest, `tools/build.mjs`(소스→runtime 복사).
10
+
11
+ ## Global Constraints
12
+
13
+ - 공통 코어는 **영어**, 6줄 이내(헤더 1문장 + 불릿 5개). 톤은 기존 preamble 의 간결 명령형.
14
+ - 단일 참조점: SSOT 는 `templates/operating-standard.md`. preamble·contract 인라인은 SSOT 의 코어 블록과 **문자 그대로 일치**해야 한다(테스트로 강제).
15
+ - 앵커 추가-read 금지(워커가 파일을 하나 더 읽게 만들지 않는다).
16
+ - `runtime/` 직접 수정 금지 — `npm run build` 로만 동기화.
17
+ - 새/수정 함수는 50 effective lines 이하.
18
+ - 정체성/경력 서사형 페르소나 문구 금지(형용사를 행동 동사로만).
19
+ - 문서/CHANGES 산문은 한국어, 코드 식별자·경로·CLI 플래그는 영어.
20
+ - 테스트 repo 루트 helper: `REPO = pathlib.Path(__file__).resolve().parents[1]`.
21
+
22
+ ---
23
+
24
+ ### Task 1: 공통 자세 SSOT 파일 신설
25
+
26
+ **Files:**
27
+ - Create: `templates/operating-standard.md`
28
+ - Test: `tests/test_operating_standard.py`
29
+
30
+ **Interfaces:**
31
+ - Produces: SSOT 파일. 이후 Task 2·3 가 이 파일의 "## Common core" 블록을 verbatim 인라인하고, Task 4 테스트가 일치를 검증한다.
32
+
33
+ - [ ] **Step 1: 실패하는 테스트 작성**
34
+
35
+ `tests/test_operating_standard.py`:
36
+ ```python
37
+ import pathlib
38
+
39
+ REPO = pathlib.Path(__file__).resolve().parents[1]
40
+ SSOT = REPO / "templates" / "operating-standard.md"
41
+
42
+
43
+ def _core_block(text: str) -> str:
44
+ """## Common core 헤딩 다음부터 다음 ## 헤딩 전까지의 본문(앞뒤 공백 제거)."""
45
+ after = text.split("## Common core", 1)[1]
46
+ return after.split("\n## ", 1)[0].strip()
47
+
48
+
49
+ def test_ssot_exists_with_core_and_addenda():
50
+ text = SSOT.read_text(encoding="utf-8")
51
+ core = _core_block(text)
52
+ assert "Evidence over assertion" in core
53
+ assert "Shortest sound path" in core
54
+ assert "Fit what's here" in core
55
+ assert "Hold your own line" in text # 워커 독립성 addendum
56
+ assert "Own the synthesis" in text # 리드 종합 addendum
57
+ ```
58
+
59
+ - [ ] **Step 2: 테스트 실패 확인**
60
+
61
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_ssot_exists_with_core_and_addenda -v`
62
+ Expected: FAIL — `FileNotFoundError` (SSOT 파일 없음)
63
+
64
+ - [ ] **Step 3: SSOT 파일 작성**
65
+
66
+ `templates/operating-standard.md`:
67
+ ```markdown
68
+ # Operating Standard (canonical)
69
+
70
+ This file is the single source of truth for the operating standard injected into
71
+ every okstra agent. The "## Common core" block below is inlined verbatim into
72
+ `templates/worker-prompt-preamble.md` (workers) and
73
+ `prompts/lead/okstra-lead-contract.md` (lead). A unit test asserts both inlined
74
+ copies match this core — update all three together.
75
+
76
+ ## Common core
77
+
78
+ Work like a senior engineer who owns this result, not a commentator on it.
79
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
80
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
81
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
82
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
83
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
84
+
85
+ ## Role addendum
86
+
87
+ - Analysis / verification workers add this line: `Hold your own line — reason from your independent angle; do not drift toward the other workers' likely answers or echo the user. Triangulation fails if your view isn't genuinely yours.`
88
+ - Lead adds this line: `Own the synthesis — weigh worker outputs on evidence, not consensus; a better-grounded dissent outranks the majority.`
89
+ - Report-writer worker: common core only (no independence addendum — it authors, it does not analyze).
90
+ ```
91
+
92
+ - [ ] **Step 4: 테스트 통과 확인**
93
+
94
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_ssot_exists_with_core_and_addenda -v`
95
+ Expected: PASS
96
+
97
+ - [ ] **Step 5: 커밋**
98
+
99
+ ```bash
100
+ git add templates/operating-standard.md tests/test_operating_standard.py
101
+ git commit -m "feat(agents): operating-standard SSOT 신설"
102
+ ```
103
+
104
+ ---
105
+
106
+ ### Task 2: 워커 preamble 에 공통 코어 + 독립성 인라인
107
+
108
+ **Files:**
109
+ - Modify: `templates/worker-prompt-preamble.md` (헤더 직후, `## Required reading` 앞)
110
+ - Test: `tests/test_operating_standard.py`
111
+
112
+ **Interfaces:**
113
+ - Consumes: Task 1 의 `## Common core` 블록(verbatim).
114
+ - Produces: 워커가 출력 전 마주치는 자세 헤더. 모든 분석/검증 워커에 적용.
115
+
116
+ - [ ] **Step 1: 실패하는 테스트 추가**
117
+
118
+ `tests/test_operating_standard.py` 에 추가:
119
+ ```python
120
+ PREAMBLE = REPO / "templates" / "worker-prompt-preamble.md"
121
+
122
+
123
+ def test_core_inlined_in_preamble():
124
+ core = _core_block(SSOT.read_text(encoding="utf-8"))
125
+ preamble = PREAMBLE.read_text(encoding="utf-8")
126
+ assert core in preamble # 코어 verbatim 일치(drift 차단)
127
+ assert "Hold your own line" in preamble # 워커 독립성 addendum
128
+ assert "Own the synthesis" not in preamble # 리드 전용 줄은 없어야
129
+ ```
130
+
131
+ - [ ] **Step 2: 테스트 실패 확인**
132
+
133
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_core_inlined_in_preamble -v`
134
+ Expected: FAIL — `assert core in preamble` (아직 인라인 안 됨)
135
+
136
+ - [ ] **Step 3: preamble 수정**
137
+
138
+ `templates/worker-prompt-preamble.md` 의 5번째 줄(`It replaces the previous practice ...` 문단) 다음, `---` 구분선 앞에 아래 섹션을 삽입한다. `## Common core` 의 본문(Step 3 Task 1 의 `Work like a senior engineer ...` ~ `... size the response to the request.` 5불릿)을 verbatim 복사한다:
139
+
140
+ ```markdown
141
+
142
+ ## Operating standard (read before any output)
143
+
144
+ Work like a senior engineer who owns this result, not a commentator on it.
145
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
146
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
147
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
148
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
149
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
150
+ - Hold your own line — reason from your independent angle; do not drift toward the other workers' likely answers or echo the user. Triangulation fails if your view isn't genuinely yours.
151
+ ```
152
+
153
+ > 주의: `_core_block` 비교는 SSOT 의 코어 본문(5불릿)이 substring 으로 들어있는지 보므로, 여기서 5불릿은 SSOT 와 한 글자도 다르면 안 된다. 독립성 줄은 코어 다음에 별도 불릿으로 붙인다.
154
+
155
+ - [ ] **Step 4: 테스트 통과 확인**
156
+
157
+ Run: `python3 -m pytest tests/test_operating_standard.py -v`
158
+ Expected: PASS (Task 1·2 테스트 모두)
159
+
160
+ - [ ] **Step 5: 커밋**
161
+
162
+ ```bash
163
+ git add templates/worker-prompt-preamble.md tests/test_operating_standard.py
164
+ git commit -m "feat(agents): 워커 preamble 에 작업 자세 + 독립성 인라인"
165
+ ```
166
+
167
+ ---
168
+
169
+ ### Task 3: 리드 contract 에 공통 코어 + 종합 인라인
170
+
171
+ **Files:**
172
+ - Modify: `prompts/lead/okstra-lead-contract.md` (`# Okstra Lead Contract` 직후, `## Overview` 앞)
173
+ - Test: `tests/test_operating_standard.py`
174
+
175
+ **Interfaces:**
176
+ - Consumes: Task 1 의 `## Common core` 블록(verbatim).
177
+ - Produces: 리드가 오케스트레이션 전 마주치는 자세 헤더.
178
+
179
+ - [ ] **Step 1: 실패하는 테스트 추가**
180
+
181
+ ```python
182
+ CONTRACT = REPO / "prompts" / "lead" / "okstra-lead-contract.md"
183
+
184
+
185
+ def test_core_inlined_in_lead_contract():
186
+ core = _core_block(SSOT.read_text(encoding="utf-8"))
187
+ contract = CONTRACT.read_text(encoding="utf-8")
188
+ assert core in contract
189
+ assert "Own the synthesis" in contract # 리드 종합 addendum
190
+ assert "Hold your own line" not in contract # 워커 전용 줄은 없어야
191
+ ```
192
+
193
+ - [ ] **Step 2: 테스트 실패 확인**
194
+
195
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_core_inlined_in_lead_contract -v`
196
+ Expected: FAIL — `assert core in contract`
197
+
198
+ - [ ] **Step 3: contract 수정**
199
+
200
+ `prompts/lead/okstra-lead-contract.md` 의 `# Okstra Lead Contract` 줄 다음, `## Overview` 앞에 삽입(코어 5불릿 verbatim + 리드 종합 줄):
201
+
202
+ ```markdown
203
+
204
+ ## Operating standard
205
+
206
+ Work like a senior engineer who owns this result, not a commentator on it.
207
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
208
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
209
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
210
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
211
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
212
+ - Own the synthesis — weigh worker outputs on evidence, not consensus; a better-grounded dissent outranks the majority.
213
+ ```
214
+
215
+ - [ ] **Step 4: 테스트 통과 확인**
216
+
217
+ Run: `python3 -m pytest tests/test_operating_standard.py -v`
218
+ Expected: PASS
219
+
220
+ - [ ] **Step 5: 커밋**
221
+
222
+ ```bash
223
+ git add prompts/lead/okstra-lead-contract.md tests/test_operating_standard.py
224
+ git commit -m "feat(agents): 리드 contract 에 작업 자세 + 종합 인라인"
225
+ ```
226
+
227
+ ---
228
+
229
+ ### Task 4: 완료 전 자기검증 게이트 sidecar 신설
230
+
231
+ **Files:**
232
+ - Create: `prompts/profiles/_implementation-self-check.md`
233
+ - Test: `tests/test_operating_standard.py`
234
+
235
+ **Interfaces:**
236
+ - Produces: 완료 전 게이트 본문. Task 5(executor/verifier 참조)·Task 6(codex_dispatch append)이 이 파일을 사용한다.
237
+
238
+ - [ ] **Step 1: 실패하는 테스트 추가**
239
+
240
+ ```python
241
+ SELFCHECK = REPO / "prompts" / "profiles" / "_implementation-self-check.md"
242
+
243
+
244
+ def test_self_check_gate_has_four_items():
245
+ text = SELFCHECK.read_text(encoding="utf-8")
246
+ assert "Implementation self-check" in text
247
+ for item in ("functions:", "conventions:", "names & comments:", "verification:"):
248
+ assert item in text
249
+ ```
250
+
251
+ - [ ] **Step 2: 테스트 실패 확인**
252
+
253
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_self_check_gate_has_four_items -v`
254
+ Expected: FAIL — `FileNotFoundError`
255
+
256
+ - [ ] **Step 3: sidecar 작성**
257
+
258
+ `prompts/profiles/_implementation-self-check.md`:
259
+ ```markdown
260
+ <!--
261
+ Completion-time counterpart to _coding-conventions-preflight.md. The preflight
262
+ gate runs BEFORE the first Edit/Write; this gate runs BEFORE the executor claims
263
+ done. Same delivery paths: Claude executor reads this file directly; codex /
264
+ antigravity executor cannot read it (outside the CLI sandbox), so the lead
265
+ appends this file's body into the persisted executor prompt at dispatch time
266
+ (see codex_dispatch.py `_implementation_executor_tail`).
267
+ -->
268
+
269
+ # Implementation self-check (BLOCKING — before you claim done)
270
+
271
+ Before declaring the change complete, write one confirming line per item to your
272
+ audit sidecar. If any item fails, fix it or surface the violation — do not claim
273
+ done on a failing item.
274
+
275
+ - functions: every new/edited function ≤50 effective lines, single purpose
276
+ - conventions: applied the routed pack + project patterns (name which ones)
277
+ - names & comments: names say what, comments say why; no obvious-restatement comments; no truthful-name violations
278
+ - verification: ran the actual build/test (paste the command + its result); no dead or commented-out code left behind
279
+ ```
280
+
281
+ - [ ] **Step 4: 테스트 통과 확인**
282
+
283
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_self_check_gate_has_four_items -v`
284
+ Expected: PASS
285
+
286
+ - [ ] **Step 5: 커밋**
287
+
288
+ ```bash
289
+ git add prompts/profiles/_implementation-self-check.md tests/test_operating_standard.py
290
+ git commit -m "feat(agents): 완료 전 자기검증 게이트 sidecar 신설"
291
+ ```
292
+
293
+ ---
294
+
295
+ ### Task 5: executor/verifier 프로필에 self-check 참조 배선
296
+
297
+ **Files:**
298
+ - Modify: `prompts/profiles/_implementation-executor.md` (preflight 불릿 다음에 self-check 불릿 추가)
299
+ - Modify: `prompts/profiles/_implementation-verifier.md` (코드 수정 시 동일 게이트 적용 declaration)
300
+ - Test: `tests/test_operating_standard.py`
301
+
302
+ **Interfaces:**
303
+ - Consumes: Task 4 의 `_implementation-self-check.md`.
304
+ - Produces: executor 가 done 전 게이트를 read/append 하도록 지시하는 계약. codex/antigravity 경로는 Task 6 가 물리적으로 append.
305
+
306
+ - [ ] **Step 1: 실패하는 테스트 추가**
307
+
308
+ ```python
309
+ EXECUTOR = REPO / "prompts" / "profiles" / "_implementation-executor.md"
310
+
311
+
312
+ def test_executor_references_self_check_gate():
313
+ text = EXECUTOR.read_text(encoding="utf-8")
314
+ assert "_implementation-self-check.md" in text
315
+ assert "before" in text.lower() and "done" in text.lower()
316
+ ```
317
+
318
+ - [ ] **Step 2: 테스트 실패 확인**
319
+
320
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_executor_references_self_check_gate -v`
321
+ Expected: FAIL — `assert "_implementation-self-check.md" in text`
322
+
323
+ - [ ] **Step 3: executor 프로필 수정**
324
+
325
+ `prompts/profiles/_implementation-executor.md` 의 Coding-conventions preflight 불릿(line 22~24 블록) **다음**에 아래 불릿을 추가한다. 문구는 preflight 불릿의 전달-경로 표현을 그대로 따른다:
326
+
327
+ ```markdown
328
+ - **Completion self-check (BLOCKING — runs BEFORE you claim the stage done).** The gate body is a single source at `prompts/profiles/_implementation-self-check.md` (sibling of this sidecar): the four-item completion gate (functions ≤50 lines, conventions applied, truthful names & why-comments, real build/test run). Do NOT re-type it from memory — deliver it by file so it cannot drift.
329
+ - **Claude executor:** Read `_implementation-self-check.md` end-to-end before appending the `status:"done"` row, then write one confirming line per item to your audit sidecar.
330
+ - **CLI executor (codex / antigravity):** the CLI process cannot Read this path (outside its sandbox). The lead appends this file's body into the persisted executor prompt at dispatch time, immediately after the preflight body (see `codex_dispatch.py` `_implementation_executor_tail`). The head-less executor honours both gates from the single prompt.
331
+ ```
332
+
333
+ - [ ] **Step 4: verifier 프로필 수정**
334
+
335
+ `prompts/profiles/_implementation-verifier.md` 끝부분에 declaration 한 줄 추가(verifier 는 보통 코드를 수정하지 않으나, 수정하는 경우 동일 게이트가 바인딩됨을 명시):
336
+
337
+ ```markdown
338
+ - **Completion self-check (declaration).** If this verifier edits project code (not just inspects it), the same completion gate at `prompts/profiles/_implementation-self-check.md` binds before claiming done — same four items, recorded in the audit sidecar.
339
+ ```
340
+
341
+ - [ ] **Step 5: 테스트 통과 확인**
342
+
343
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_executor_references_self_check_gate -v`
344
+ Expected: PASS
345
+
346
+ - [ ] **Step 6: 커밋**
347
+
348
+ ```bash
349
+ git add prompts/profiles/_implementation-executor.md prompts/profiles/_implementation-verifier.md tests/test_operating_standard.py
350
+ git commit -m "feat(agents): executor/verifier 에 완료 전 self-check 배선"
351
+ ```
352
+
353
+ ---
354
+
355
+ ### Task 6: codex_dispatch 가 self-check body 를 CLI 프롬프트에 append
356
+
357
+ **Files:**
358
+ - Modify: `scripts/okstra_ctl/codex_dispatch.py:802-812` (`_implementation_executor_tail`)
359
+ - Test: `tests/test_operating_standard.py`
360
+
361
+ **Interfaces:**
362
+ - Consumes: Task 4 의 `_implementation-self-check.md`.
363
+ - Produces: codex/antigravity executor 의 persisted 프롬프트 tail 에 preflight body + self-check body 가 순서대로 포함.
364
+
365
+ - [ ] **Step 1: 실패하는 테스트 추가**
366
+
367
+ `_implementation_executor_tail` 은 `workspace_root` 의 `prompts/profiles/` 를 읽으므로, repo 자체를 workspace_root 로 준다. 최소 manifest 는 `taskType` 만 필요(`_require_string(manifest, "taskType")`).
368
+ ```python
369
+ from scripts.okstra_ctl import codex_dispatch
370
+
371
+
372
+ def test_executor_tail_includes_both_gates():
373
+ tail = codex_dispatch._implementation_executor_tail(
374
+ {"taskType": "implementation"}, REPO, "executor"
375
+ )
376
+ assert "Coding-conventions preflight" in tail # 기존 preflight 유지
377
+ assert "Implementation self-check" in tail # 신규 self-check 추가
378
+ ```
379
+ > import 경로는 기존 codex_dispatch 테스트(`tests/test_*dispatch*.py`)의 import 방식을 따른다. 없으면 `importlib`로 `scripts/okstra_ctl/codex_dispatch.py`를 로드한다.
380
+
381
+ - [ ] **Step 2: 테스트 실패 확인**
382
+
383
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_executor_tail_includes_both_gates -v`
384
+ Expected: FAIL — `assert "Implementation self-check" in tail` (아직 self-check 미append)
385
+
386
+ - [ ] **Step 3: `_implementation_executor_tail` 수정**
387
+
388
+ `scripts/okstra_ctl/codex_dispatch.py` 의 함수를 아래로 교체(preflight 뒤에 self-check body 를 이어붙임, 50줄 이내 유지):
389
+ ```python
390
+ def _implementation_executor_tail(
391
+ manifest: Mapping[str, Any],
392
+ workspace_root: Path,
393
+ role: str,
394
+ ) -> str:
395
+ if role != "executor" or _require_string(manifest, "taskType") != "implementation":
396
+ return ""
397
+ profiles = workspace_root / "prompts" / "profiles"
398
+ parts: list[str] = []
399
+ preflight_path = profiles / "_coding-conventions-preflight.md"
400
+ if preflight_path.is_file():
401
+ parts.append(preflight_path.read_text(encoding="utf-8"))
402
+ else:
403
+ parts.append("# Coding-conventions preflight\n\nApply project-local conventions before editing.")
404
+ selfcheck_path = profiles / "_implementation-self-check.md"
405
+ if selfcheck_path.is_file():
406
+ parts.append(selfcheck_path.read_text(encoding="utf-8"))
407
+ return "\n\n".join(parts)
408
+ ```
409
+
410
+ - [ ] **Step 4: 테스트 통과 확인**
411
+
412
+ Run: `python3 -m pytest tests/test_operating_standard.py::test_executor_tail_includes_both_gates -v`
413
+ Expected: PASS
414
+
415
+ - [ ] **Step 5: 커밋**
416
+
417
+ ```bash
418
+ git add scripts/okstra_ctl/codex_dispatch.py tests/test_operating_standard.py
419
+ git commit -m "feat(agents): CLI executor 프롬프트에 self-check body append"
420
+ ```
421
+
422
+ ---
423
+
424
+ ### Task 7: 빌드·전체 검증·CHANGES 기록
425
+
426
+ **Files:**
427
+ - Modify: `CHANGES.md` (최상단 날짜 섹션)
428
+ - (build output) `runtime/`
429
+
430
+ - [ ] **Step 1: 빌드(소스→runtime 동기화)**
431
+
432
+ Run: `npm run build`
433
+ Expected: `BUILD OK` 류 출력, 에러 없음. 새 파일(`operating-standard.md`, `_implementation-self-check.md`)이 `runtime/` 로 복사됨.
434
+
435
+ - [ ] **Step 2: runtime 동기화 확인**
436
+
437
+ ```bash
438
+ grep -rl "Operating Standard (canonical)" runtime/ | head
439
+ grep -rl "Implementation self-check" runtime/ | head
440
+ ```
441
+ Expected: 각각 runtime 하위 경로가 출력됨.
442
+
443
+ - [ ] **Step 3: 전체 테스트 + 워크플로 검증**
444
+
445
+ Run:
446
+ ```bash
447
+ python3 -m pytest tests/ -q
448
+ bash validators/validate-workflow.sh
449
+ ```
450
+ Expected: 전체 PASS, validator 통과.
451
+
452
+ - [ ] **Step 4: CHANGES.md 기록**
453
+
454
+ `CHANGES.md` 최상단에 `## 2026-06-19` 섹션이 이미 있으면 그 아래, 없으면 새로 추가하여 항목 작성:
455
+ ```markdown
456
+ ### feat(agents): 전 에이전트 공통 '작업 자세' + 코드 워커 완료 전 자기검증 게이트
457
+
458
+ - **배경**: 모든 okstra 에이전트의 출력 품질·태도·일관성·코드 작성 품질을 끌어올리고 싶었다. 코드 품질 규약(함수 50줄·정직한 이름·패턴 적용·증거 기반 완료)은 이미 존재했으므로, 문제는 규약 부재가 아니라 이행이었다.
459
+ - **해결**: 형용사를 검증 가능한 행동으로 번역한 공통 '작업 자세' 코어를 `templates/operating-standard.md` SSOT 로 두고, 워커 preamble·리드 contract 에 verbatim 인라인했다(테스트로 drift 차단). 워커는 독립성 한 줄, 리드는 근거 기반 종합 한 줄을 덧붙여 cross-verification 의 관점 다양성을 보존한다. 코드 워커에는 시작 전 preflight 의 짝인 완료 전 자기검증 게이트(`_implementation-self-check.md`)를 추가해, executor 가 done 선언 전 함수 길이·패턴·네이밍·실제 검증을 audit 사이드카에 한 줄씩 확정하게 했다(codex/antigravity 는 lead 가 프롬프트에 append).
460
+ - 사용자 영향: okstra-run 의 분석·구현·검증 전 단계에서 에이전트가 더 일관되게 증거 기반으로 답하고, 코드 작성 결과가 '검증 없이 완료 선언'·'함수 비대'·'패턴 무시' 없이 나오도록 자기검증을 강제한다. (설치 환경은 `okstra install` 로 런타임 갱신 후 적용.)
461
+ ```
462
+
463
+ - [ ] **Step 5: 커밋**
464
+
465
+ ```bash
466
+ git add CHANGES.md runtime/
467
+ git commit -m "chore(agents): operating-standard 빌드 동기화 + CHANGES 기록"
468
+ ```
469
+
470
+ ---
471
+
472
+ ## Self-Review (작성자 체크)
473
+
474
+ - **Spec coverage**: §4.1 공통 헤더→Task 1·2·3 / §4.2 역할 분기→Task 2(워커)·3(리드)·1(report-writer 명시) / §4.3 완료 전 게이트→Task 4·5·6 / §4.4 단일 참조점·주입 지점→Task 1(SSOT)·2·3(인라인)·6(append) / §5 검증→각 Task 테스트 + Task 7. 누락 없음.
475
+ - **Placeholder scan**: 모든 코드/문구 블록은 실제 내용. TBD/TODO 없음.
476
+ - **Type consistency**: 테스트 helper `_core_block`·상수(`SSOT`/`PREAMBLE`/`CONTRACT`/`SELFCHECK`/`EXECUTOR`)는 Task 1 에서 정의 후 재사용, 함수 시그니처 `_implementation_executor_tail(manifest, workspace_root, role)` 는 현재 코드와 일치.
@@ -0,0 +1,120 @@
1
+ # 에이전트 작업 자세(Operating Standard) 주입 설계
2
+
3
+ - 날짜: 2026-06-19
4
+ - 상태: 설계 승인 대기
5
+ - 범위: okstra 전 에이전트(분석 워커 / executor·verifier / report-writer / 리드)에 공통 "작업 자세" 행동 규약을 주입하고, 코드 작성 워커에 완료 전 자기검증 게이트를 추가한다.
6
+
7
+ ## 1. 배경과 목적
8
+
9
+ 사용자가 모든 okstra 에이전트에 "노련하고 효율적이고 성실하고 전문가스러운" 태도를 주입하길 원했다. 출력에서 아쉬웠던 점은 깊이·정확도, 태도·성의, 일관성, 장황함, 그리고 특히 **코드 작성 품질**(함수 길이/복잡, 프로젝트 패턴 무시, 주석·네이밍 부실, 검증 없이 완료 선언) 전반이었다.
10
+
11
+ ## 2. 핵심 통찰 — "규약 부재"가 아니라 "이행 부재"
12
+
13
+ 사용자가 지목한 코드 품질 4개는 **이미 규약에 전부 존재**한다:
14
+
15
+ - 함수 50줄 상한 → 글로벌 `CLAUDE.md` 4.5, [_coding-conventions-preflight.md](../../../prompts/profiles/_coding-conventions-preflight.md) "single-purpose functions ≤50 effective lines"
16
+ - 프로젝트 패턴 따르기 → `CLAUDE.md` 3.1, preflight 의 routed pack + project review packs
17
+ - 이름=무엇 / 주석=왜 → `CLAUDE.md` 4.1, preflight 의 "truthful names"
18
+ - 증거 기반 완료 → `CLAUDE.md` 2.6·7장, preflight 의 "lint/test green 은 필요조건일 뿐"
19
+
20
+ 따라서 규약 텍스트를 늘리는 것은 역효과다(안 읽히는 글만 길어진다). 설계는 **"얼마나 자주 마주치고, 출력 전에 스스로 검증하게 만드느냐"**(이행률)로 향한다. 막연한 정체성 페르소나("당신은 20년 경력 전문가다")는 현대 모델에서 추론 능력을 끌어올리지 못하고 장황함·과신·아첨을 유발하므로 채택하지 않는다 — 형용사를 **검증 가능한 동사**로 번역한다.
21
+
22
+ ## 3. 범위
23
+
24
+ ### 포함
25
+ - 전 에이전트 공통 "작업 자세" 헤더(형용사 → 행동 규약).
26
+ - 역할별 한 줄 분기(워커 독립성 / 리드 종합 / report-writer 제외).
27
+ - 코드 작성 워커(executor·verifier)의 **완료 전 자기검증 게이트** — 기존 시작 전 preflight 의 짝.
28
+
29
+ ### 비포함 (NOT)
30
+ - 정체성/경력 서사형 페르소나 문구.
31
+ - validator 기반 기계 강제(C안) — 정적으로 잡히는 항목이 제한적이라 본 작업에서 제외. 자기보고(self-check) 방식으로 간다.
32
+ - 워커별로 서로 다른 페르소나(관점 다양성은 독립성 조항으로 보존하되, 자세 규약 자체는 통일).
33
+
34
+ ## 4. 설계
35
+
36
+ ### 4.1 공통 작업 자세 헤더 (SSOT, 영어)
37
+
38
+ 기존 preamble·contract 가 영어이고 워커 출력 언어와 무관하므로 영어로 둔다. 톤은 기존 preamble 의 간결 명령형을 따른다.
39
+
40
+ ```
41
+ ## Operating standard
42
+
43
+ Work like a senior engineer who owns this result, not a commentator on it.
44
+ - Evidence over assertion — back every claim with a file:line, or mark it an
45
+ explicit assumption. Never state the unverified as fact.
46
+ - Read before you reason — read each required input end to end; when you lack
47
+ basis, write "insufficient evidence" instead of filling the gap plausibly.
48
+ - Shortest sound path — chase the most likely cause first; don't re-verify what
49
+ is settled or pad with restatement.
50
+ - Decide, don't survey — when options exist, give the trade-off and one
51
+ recommendation, not an exhaustive list.
52
+ - Fit what's here — match the surrounding code and prose; size the response to
53
+ the request.
54
+ ```
55
+
56
+ ### 4.2 역할별 한 줄 분기 (동질화 방지)
57
+
58
+ cross-verification 은 세 워커의 관점이 **독립적**이어야 triangulation 이 성립한다. 자세 규약은 통일하되 관점은 보존한다.
59
+
60
+ - **분석/검증 워커 전용** 추가 한 줄:
61
+ `Hold your own line — reason from your independent angle; do not drift toward the other workers' likely answers or echo the user. Triangulation fails if your view isn't genuinely yours.`
62
+ - **리드 전용** 추가 한 줄:
63
+ `Own the synthesis — weigh worker outputs on evidence, not consensus; a better-grounded dissent outranks the majority.`
64
+ - **report-writer**: 독립성 조항 없음(독립 분석을 하지 않는 작성자). 공통 코어만 적용.
65
+
66
+ ### 4.3 코드 워커 완료 전 자기검증 게이트
67
+
68
+ [_coding-conventions-preflight.md](../../../prompts/profiles/_coding-conventions-preflight.md)(시작 전 BLOCKING)에 대응하는 **완료 전 BLOCKING** 게이트. executor·verifier 가 "done" 을 선언하기 직전, audit 사이드카에 항목별 한 줄 확정을 쓴다. 항목은 사용자가 지목한 4개에 1:1 대응한다.
69
+
70
+ ```
71
+ ## Implementation self-check (BLOCKING — before you claim done)
72
+
73
+ Before declaring the change complete, write one confirming line per item to your
74
+ audit sidecar. If any fails, fix it or surface the violation — do not claim done.
75
+ - functions: every new/edited function ≤50 effective lines, single purpose
76
+ - conventions: applied the routed pack + project patterns (name them)
77
+ - names & comments: names say what, comments say why; no obvious-restatement
78
+ comments; no truthful-name violations
79
+ - verification: ran the actual build/test (paste command + result); no
80
+ dead/commented-out code left
81
+ ```
82
+
83
+ 이 게이트는 시작 전 preflight 와 **별개 관심사**(완료 전 검증)이므로 별도 sidecar `prompts/profiles/_implementation-self-check.md` 로 신설한다. 전달 경로는 preflight 와 동일하다: Claude executor 는 직접 read, codex/antigravity executor 는 sidecar 를 read 할 수 없으므로 lead 가 dispatch 시 프롬프트에 본문을 append 한다. [_implementation-executor.md](../../../prompts/profiles/_implementation-executor.md)·[_implementation-verifier.md](../../../prompts/profiles/_implementation-verifier.md) 가 이 sidecar 를 완료 전 단계에서 참조하도록 배선한다.
84
+
85
+ ### 4.4 주입 지점과 단일 참조점
86
+
87
+ build.mjs 는 단순 복사이고(파일 include 합성 없음), 워커는 `**Worker Preamble Path:**` 앵커로 [worker-prompt-preamble.md](../../../templates/worker-prompt-preamble.md)를 통째로 read 한다. 리드는 [okstra-lead-contract.md](../../../prompts/lead/okstra-lead-contract.md)·[team-contract.md](../../../prompts/lead/team-contract.md)를 읽는다.
88
+
89
+ 공통 코어(4.1)는 두 호출자(워커 preamble·리드 contract)를 가지므로 추출 대상이다. 실현 방식:
90
+
91
+ - **채택**: `templates/operating-standard.md` 를 SSOT 로 신설(4.1 코어 + 역할 분기 규칙 명시). 워커 preamble 과 리드 contract 는 코어 문장을 **인라인**한다(앵커 추가-read 없음 — okstra 는 audience-scoped enumeration 을 성능 최적화로 명시할 만큼 워커 토큰에 민감하므로, 5줄짜리 코어를 위해 매 워커 +1 파일 read 를 추가하지 않는다). 인라인 중복은 **단위 테스트로 drift 를 차단**한다(세 위치의 코어 문장이 SSOT 와 일치하는지 grep 검증).
92
+ - 코드 워커 게이트(4.3)는 별도 sidecar `prompts/profiles/_implementation-self-check.md` 로 신설한다(preflight 와 별개 관심사).
93
+
94
+ 주입 대상 파일:
95
+
96
+ | 파일 | 추가 내용 |
97
+ |---|---|
98
+ | `templates/operating-standard.md` (신설) | 공통 코어 + 역할 분기 규칙 (SSOT) |
99
+ | [templates/worker-prompt-preamble.md](../../../templates/worker-prompt-preamble.md) | 공통 코어 인라인 + 워커 독립성 한 줄 |
100
+ | [prompts/lead/okstra-lead-contract.md](../../../prompts/lead/okstra-lead-contract.md) | 공통 코어 인라인 + 리드 종합 한 줄 |
101
+ | `prompts/profiles/_implementation-self-check.md` (신설) | 완료 전 자기검증 게이트 |
102
+ | [_implementation-executor.md](../../../prompts/profiles/_implementation-executor.md) · [_implementation-verifier.md](../../../prompts/profiles/_implementation-verifier.md) | 완료 전 self-check sidecar 참조 배선 |
103
+
104
+ ## 5. 검증 / 테스트
105
+
106
+ - **단위 테스트(신규)**: `operating-standard.md` 의 코어 문장이 worker-prompt-preamble.md 와 okstra-lead-contract.md 에 모두 존재하는지(동기화), 워커 독립성 한 줄이 preamble 에만·리드 종합 한 줄이 contract 에만 있는지 검증.
107
+ - **단위 테스트(신규)**: 코드 워커 자기검증 게이트의 4개 항목 헤딩이 preflight 전달 경로에 포함되는지 검증.
108
+ - `npm run build` 후 `runtime/` 동기화 확인.
109
+ - `bash validators/validate-workflow.sh` 통과.
110
+ - `python3 -m pytest tests/` 전체 통과.
111
+
112
+ ## 6. 사용자 영향 (CHANGES.md 예정)
113
+
114
+ okstra 의 모든 에이전트가 출력 전 공통 "작업 자세"(증거 우선·끝까지 읽기·최단 경로·결정 후 추천·주변에 맞추기)를 마주치고, 코드 작성 워커는 "done" 선언 전 함수 길이·패턴·네이밍·실제 검증을 audit 사이드카에 자기보고한다. 워커 독립성 조항으로 cross-verification 의 관점 다양성은 보존된다.
115
+
116
+ ## 7. 리스크 / 미해결
117
+
118
+ - 자기검증은 self-check 이므로 100% 보장이 아니다(설계상 수용 — 기계 강제는 비포함). 다만 audit 사이드카 자기보고는 "조용히 건너뛰기"를 어렵게 만든다.
119
+ - 공통 코어 인라인 중복은 테스트로 drift 를 막지만, 테스트가 코어 문장을 문자열로 들고 있어야 한다(SSOT 변경 시 테스트도 갱신). 수용 가능한 트레이드오프.
120
+ - 헤더가 기존 preamble/contract 의 분량을 늘린다(코어 ~6줄 + 분기 1줄). 토큰 영향은 미미하나, 기존 "장황함 줄이기" 목표와 상충하지 않도록 코어를 6줄 이내로 유지한다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.92.1",
3
+ "version": "0.94.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.92.1",
3
- "builtAt": "2026-06-18T08:35:38.504Z",
2
+ "package": "0.94.0",
3
+ "builtAt": "2026-06-18T18:35:57.621Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -93,7 +93,7 @@ The wrapper exists because Claude Code's Bash permission matcher rejects simple-
93
93
  - Do NOT abort the loop on transient `running` status. Only `completed` or the polling cap (initially 30min, optionally extended once to 35min by mtime grace) end it.
94
94
  - **No external timeout from Lead.** This polling loop is the SINGLE timeout authority for this dispatch. Lead MUST NOT impose a separate Agent-call timeout that would terminate this subagent before the polling cap is reached (see team-contract "No external timeout on wrapper subagents").
95
95
  - Do NOT issue parallel `BashOutput` calls or speculate about progress between polls.
96
- - **No standalone `sleep` between polls.** The harness blocks `sleep` calls of 5 seconds or longer as a circumvention vector and explicitly forbids chaining shorter sleeps to work around it. `BashOutput` itself is the wait primitive — calling it again immediately after a `running` status is correct. If you find yourself wanting to "slow down" the loop, that desire is a leftover from the retired 60-second-cadence rule and should be ignored.
96
+ - **No standalone `sleep` between polls.** The harness blocks `sleep` calls of 5 seconds or longer as a circumvention vector and explicitly forbids chaining shorter sleeps to work around it. `BashOutput` itself is the wait primitive — calling it again immediately after a `running` status is correct.
97
97
 
98
98
  8. After the polling loop exits with `completed`, perform terminal-status determination BEFORE returning:
99
99
 
@@ -101,7 +101,7 @@ The wrapper exists because Claude Code's Bash permission matcher rejects simple-
101
101
 
102
102
  b. **CLI failure first.** If the final `BashOutput` reports a non-zero `exit_code`, follow the **CLI failure** rule in §"Error reporting" before returning. Do NOT perform the result-file check on a failed exit — `cli-failure` already covers it.
103
103
 
104
- c. **Result-file existence check (exit 0 only).** If `exit_code == 0` BUT no file exists at the extracted Result Path, the Antigravity CLI returned 0 without producing the analysis artifact. Observed failure mode: the CLI streams analysis prose on stdout, hits its token budget or a sandbox EPERM mid-`Write`, and exits 0 with the artifact never persisted. Forwarding the partial stdout silently degrades lead synthesis (the case that motivated this rule), so this path is required.
104
+ c. **Result-file existence check (exit 0 only).** If `exit_code == 0` BUT no file exists at the extracted Result Path, the Antigravity CLI returned 0 without producing the analysis artifact (it streamed prose, hit its token budget or a sandbox EPERM mid-`Write`, and exited 0 with the artifact never persisted). Forwarding the partial stdout degrades lead synthesis, so this path is required.
105
105
  1. Capture the final ~10 lines of the wrapper's live log for diagnostics — single Bash call: `tail -n 10 "${prompt_path%.md}.log"` (substitute the literal absolute prompt-history path; the wrapper writes the log next to it per the §"trace pane" comment in `okstra-antigravity-exec.sh`). Write the captured lines to a temp file (e.g. `<errors-sidecar-dir>/agy-result-missing-tail.txt`) so `--stderr-excerpt-file` can reference it.
106
106
  2. Record a `cli-failure` event directly to the run-level error log via the exact `okstra error-log append-observed` template in §"Error reporting" — substitute `--exit-code 0`, `--duration-ms <observed-ms>`, `--message "okstra-antigravity-exec.sh exited 0 but no result file at <abs-path>"`, and `--stderr-excerpt-file <temp-tail-path>`.
107
107
  3. Return `ANTIGRAVITY_RESULT_MISSING: agy exited 0 but result file absent at <abs-path>` instead of the raw stdout. The lead is responsible for deciding redispatch per `team-contract` "Lead Redispatch Policy on Result-Missing".
@@ -51,9 +51,7 @@ Unlike the Codex / Antigravity workers, you are an in-process Claude subagent
51
51
 
52
52
  5. **MCP usage**: The canonical list of MCP servers and tools available for this run lives in the lead prompt's `## Available MCP Servers` section (sourced from `.okstra/project.json`'s `mcpServers` array). When the task requires inspection of an external system covered by one of those servers, call the listed tool directly by name (e.g. `mcp__<server>__<tool>`). Do NOT shell out via `claude --mcp-cli call ...` or run the tool name as a Bash command — those are not valid invocation paths. If a server you need is not listed, record `MCP not available for this run` in your worker output rather than guessing a tool name.
53
53
 
54
- 6. If the task brief includes an `## Available MCP Servers` section in the lead prompt, treat that as the canonical list of MCP tools you may invoke for this run. If a needed server is not listed, record `MCP not available for this run` rather than calling it.
55
-
56
- 7. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/validate-improvement-report.py`.
54
+ 6. When `Task Type` is `improvement-discovery`, the lead's Phase 1.5 reflect-back log at `<RUN_DIR>/state/phase-1.5-grilling.md` is the authoritative scope and lens definition. Read its `Resolved scope` and `Resolved lenses` blocks and do NOT re-interpret the brief's raw `scan-scope` / `priority-lenses` fields. Findings that violate the resolved lens whitelist or scope are rejected by `validators/validate-improvement-report.py`.
57
55
 
58
56
  ## Required Reading Before Any Analysis
59
57
 
@@ -93,7 +93,7 @@ The wrapper exists because Claude Code's Bash permission matcher rejects simple-
93
93
  - Do NOT abort the loop on transient `running` status. Only `completed` or the polling cap (initially 30min, optionally extended once to 35min by mtime grace) end it.
94
94
  - **No external timeout from Lead.** This polling loop is the SINGLE timeout authority for this dispatch. Lead MUST NOT impose a separate Agent-call timeout that would terminate this subagent before the polling cap is reached (see team-contract "No external timeout on wrapper subagents").
95
95
  - Do NOT issue parallel `BashOutput` calls or speculate about progress between polls.
96
- - **No standalone `sleep` between polls.** The harness blocks `sleep` calls of 5 seconds or longer as a circumvention vector and explicitly forbids chaining shorter sleeps to work around it. `BashOutput` itself is the wait primitive — calling it again immediately after a `running` status is correct. If you find yourself wanting to "slow down" the loop, that desire is a leftover from the retired 60-second-cadence rule and should be ignored.
96
+ - **No standalone `sleep` between polls.** The harness blocks `sleep` calls of 5 seconds or longer as a circumvention vector and explicitly forbids chaining shorter sleeps to work around it. `BashOutput` itself is the wait primitive — calling it again immediately after a `running` status is correct.
97
97
 
98
98
  8. After the polling loop exits with `completed`, perform terminal-status determination BEFORE returning:
99
99
 
@@ -101,7 +101,7 @@ The wrapper exists because Claude Code's Bash permission matcher rejects simple-
101
101
 
102
102
  b. **CLI failure first.** If the final `BashOutput` reports a non-zero `exit_code`, follow the **CLI failure** rule in §"Error reporting" before returning. Do NOT perform the result-file check on a failed exit — `cli-failure` already covers it.
103
103
 
104
- c. **Result-file existence check (exit 0 only).** If `exit_code == 0` BUT no file exists at the extracted Result Path, the Codex CLI returned 0 without producing the analysis artifact. Observed failure mode: the CLI streams analysis prose on stdout, hits its token budget or a sandbox EPERM mid-`Write`, and exits 0 with the artifact never persisted. Forwarding the partial stdout silently degrades lead synthesis (the case that motivated this rule), so this path is required.
104
+ c. **Result-file existence check (exit 0 only).** If `exit_code == 0` BUT no file exists at the extracted Result Path, the Codex CLI returned 0 without producing the analysis artifact (it streamed prose, hit its token budget or a sandbox EPERM mid-`Write`, and exited 0 with the artifact never persisted). Forwarding the partial stdout degrades lead synthesis, so this path is required.
105
105
  1. Capture the final ~10 lines of the wrapper's live log for diagnostics — single Bash call: `tail -n 10 "${prompt_path%.md}.log"` (substitute the literal absolute prompt-history path; the wrapper writes the log next to it per the §"trace pane" comment in `okstra-codex-exec.sh`). Write the captured lines to a temp file (e.g. `<errors-sidecar-dir>/codex-result-missing-tail.txt`) so `--stderr-excerpt-file` can reference it.
106
106
  2. Record a `cli-failure` event directly to the run-level error log via the exact `okstra error-log append-observed` template in §"Error reporting" — substitute `--exit-code 0`, `--duration-ms <observed-ms>`, `--message "okstra-codex-exec.sh exited 0 but no result file at <abs-path>"`, and `--stderr-excerpt-file <temp-tail-path>`.
107
107
  3. Return `CODEX_RESULT_MISSING: codex exited 0 but result file absent at <abs-path>` instead of the raw stdout. The lead is responsible for deciding redispatch per `team-contract` "Lead Redispatch Policy on Result-Missing".
@@ -110,12 +110,6 @@ Write the data.json (and the audit sidecar `.md`) with your `Write` tool — tha
110
110
  data.json written to <abs path>; markdown rendered to <abs path>. Sections populated: <count>.
111
111
  ```
112
112
 
113
- <!-- Worker Result File contract lives above, right after the Authority
114
- section. The legacy "after Authoring Contract" placement was kept
115
- for one cycle as a courtesy to readers who learned the old layout;
116
- remove this marker after v0.32. -->
117
-
118
-
119
113
  ## Error reporting
120
114
 
121
115
  If any tool call fails (Write of the prompt history file, mkdir, MCP call, Read of an input file), append a `tool-failure` entry to the worker errors sidecar at:
@@ -1,5 +1,15 @@
1
1
  # Okstra Lead Contract
2
2
 
3
+ ## Operating standard
4
+
5
+ Work like a senior engineer who owns this result, not a commentator on it.
6
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
7
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
8
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
9
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
10
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
11
+ - Own the synthesis — weigh worker outputs on evidence, not consensus; a better-grounded dissent outranks the majority.
12
+
3
13
  ## Overview
4
14
 
5
15
  Claude lead orchestrates multiple AI workers (Claude, Codex, Antigravity) against a prepared task bundle, collects their independent outputs, supervises convergence, and ensures the final report is produced. When `Report writer worker` is in the selected roster, the final report file is **authored by the Report writer worker** — Claude lead reviews and approves but does not write the file itself. Claude lead never substitutes its own reasoning for a worker result, and never bypasses a rostered Report writer worker by writing the report directly.
@@ -223,7 +233,7 @@ If the launch prompt contains `Tmux Worker Dispatch Gate`, Phase 3 is recorded a
223
233
 
224
234
  Spawn **analysis workers only** in the same turn (Phase 4 in Teams mode; Phase 5 with `run_in_background: true` and no `team_name` when Teams unavailable). Preserve exact roster, role labels, assigned models from the task bundle.
225
235
 
226
- **Agent `name` on dispatch (BLOCKING — token-usage attribution depends on it).** Every analysis-worker `Agent(...)` call MUST set `name: "<workerId>-worker"` — `name: "claude-worker"` / `name: "codex-worker"` / `name: "antigravity-worker"` — exactly as the report-writer dispatch sets `name: "report-writer"` ([report-writer](./report-writer.md)). The Agent harness records this `name` as `agentName` in the subagent session jsonl, and the Phase 7 token collector matches each worker's session by that `agentName` (`okstra_token_usage/collect.py`). A worker dispatched **without** `name` produces a session with no `agentName`; the collector cannot attribute it and records the worker as `source: "unavailable"` even though the session exists and is team-tagged (observed in `dev-9692` error-analysis: `claude`/`codex` workers dispatched without `name` → both `unavailable`, while the named `report-writer` collected normally). Convergence reverify dispatches keep the prefix (`<workerId>-worker-reverify-r<N>`). For `implementation` and `final-verification` dispatches the `name` carries the **functional role** so the FleetView teammate pill and trace panes name the actual job instead of a generic `worker`: the Executor dispatch sets `name: "<workerId>-executor"` (e.g. `codex-executor`) and every verifier dispatch sets `name: "<workerId>-verifier"` (e.g. `claude-verifier`). These role suffixes still attribute correctly — the collector matches any `agentName` beginning `<workerId>-` (`okstra_token_usage/collect.py` `match_prefixes`). **For every CLI (Codex / Antigravity) dispatch, Lead MUST inject a `**Pane role:**` line into the dispatched prompt body whose value is the Agent `name` with the leading `<cli>-` prefix removed** — the wrapper subagent reads it and passes it as the wrapper's 5th `<role>` argument so the tmux trace pane title reads `<cli>-<role>` (which equals the FleetView teammate name) instead of a generic `<cli>-worker` (see `agents/workers/_cli-wrapper-template.md`). So `name: "codex-worker"` → `**Pane role:** worker`, `name: "codex-worker-reverify-r1"` → `**Pane role:** worker-reverify-r1`, `name: "codex-executor"` → `**Pane role:** executor`, `name: "codex-verifier"` → `**Pane role:** verifier`. The wrapper defaults to `worker` when the line is absent, but always inject it so the pane names the actual job.
236
+ **Agent `name` on dispatch (BLOCKING — token-usage attribution depends on it).** Every analysis-worker `Agent(...)` call MUST set `name: "<workerId>-worker"` — `name: "claude-worker"` / `name: "codex-worker"` / `name: "antigravity-worker"` — exactly as the report-writer dispatch sets `name: "report-writer"` ([report-writer](./report-writer.md)). The Agent harness records this `name` as `agentName` in the subagent session jsonl, and the Phase 7 token collector matches each worker's session by that `agentName` (`okstra_token_usage/collect.py`). A worker dispatched **without** `name` produces a session with no `agentName`; the collector cannot attribute it and records the worker as `source: "unavailable"` even though the session exists and is team-tagged. Convergence reverify dispatches keep the prefix (`<workerId>-worker-reverify-r<N>`). For `implementation` and `final-verification` dispatches the `name` carries the **functional role** so the FleetView teammate pill and trace panes name the actual job instead of a generic `worker`: the Executor dispatch sets `name: "<workerId>-executor"` (e.g. `codex-executor`) and every verifier dispatch sets `name: "<workerId>-verifier"` (e.g. `claude-verifier`). These role suffixes still attribute correctly — the collector matches any `agentName` beginning `<workerId>-` (`okstra_token_usage/collect.py` `match_prefixes`). **For every CLI (Codex / Antigravity) dispatch, Lead MUST inject a `**Pane role:**` line into the dispatched prompt body whose value is the Agent `name` with the leading `<cli>-` prefix removed** — the wrapper subagent reads it and passes it as the wrapper's 5th `<role>` argument so the tmux trace pane title reads `<cli>-<role>` (which equals the FleetView teammate name) instead of a generic `<cli>-worker` (see `agents/workers/_cli-wrapper-template.md`). So `name: "codex-worker"` → `**Pane role:** worker`, `name: "codex-worker-reverify-r1"` → `**Pane role:** worker-reverify-r1`, `name: "codex-executor"` → `**Pane role:** executor`, `name: "codex-verifier"` → `**Pane role:** verifier`. The wrapper defaults to `worker` when the line is absent, but always inject it so the pane names the actual job.
227
237
 
228
238
  **Agent `model:` on dispatch (BLOCKING — assignment is otherwise ignored).** The `Claude worker` `Agent(...)` call MUST set `model: "<family token of that role's modelExecutionValue>"` (`fable` / `opus` / `sonnet` / `haiku`), per [team-contract](./team-contract.md) "Model Assignment Rules" #3–#4. The claude-worker definition is `model: inherit`, so omitting this parameter makes the worker silently run on the lead's model instead of its manifest assignment — the assigned-vs-actual deviation. `Codex worker` / `Antigravity worker` are exempt: their CLI model is applied via the wrapper's own `--model` argument, so leave their Agent `model:` at `inherit` (rule #5).
229
239
 
@@ -383,7 +393,7 @@ After persistence, reply briefly in the resolved Report Language with: completio
383
393
  | Letting `convergence.maxRounds` default to 2 for `requirements-discovery` | Resolve effective default to `1` for discovery and record in convergence state artifact |
384
394
  | Issuing serial Read calls in Phase 1 | The intake files are independent — issue all Read calls in a single message (parallel) |
385
395
  | Flagging the claude-worker dispatch prompt as "incomplete" because it lacks `[Required reading]` / `[Error reporting]` blocks | Intentional asymmetry — see [team-contract](./team-contract.md) "Asymmetry between claude-worker and codex/antigravity-worker prompts" |
386
- | Waiting silently while the dispatched `claude-worker` Agent call returns nothing for many minutes (the dev-9495 pattern: two 28+25-minute hangs before lead manually `tmux kill-pane`d) | The claude-worker MUST append a `- PROGRESS: <stage> <ISO-UTC>` line to its audit sidecar (`runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md`) at least every 5 minutes (see `agents/workers/claude-worker.md` "Heartbeat" rule). If the sidecar is absent or its mtime is >5 minutes stale, treat the dispatch as `timeout` and redispatch once with a byte-identical prompt; after a second silent hang, record terminal status `timeout` with the missing-sidecar reason in team-state. The authoritative completion signal is the **result file's appearance**, detected via self-scheduled polling (see [team-contract](./team-contract.md) "Worker-completion detection (self-scheduled polling)") — NOT the Agent-call return, which under `team_name` dispatch is just an immediate `Spawned successfully` ack. The heartbeat sidecar is an auxiliary liveness signal layered on top: a missing sidecar after the result file appears is itself a contract violation per the heartbeat rule |
396
+ | Waiting silently while the dispatched `claude-worker` Agent call returns nothing for many minutes | The claude-worker MUST append a `- PROGRESS: <stage> <ISO-UTC>` line to its audit sidecar (`runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md`) at least every 5 minutes (see `agents/workers/claude-worker.md` "Heartbeat" rule). If the sidecar is absent or its mtime is >5 minutes stale, treat the dispatch as `timeout` and redispatch once with a byte-identical prompt; after a second silent hang, record terminal status `timeout` with the missing-sidecar reason in team-state. The authoritative completion signal is the **result file's appearance**, detected via self-scheduled polling (see [team-contract](./team-contract.md) "Worker-completion detection (self-scheduled polling)") — NOT the Agent-call return, which under `team_name` dispatch is just an immediate `Spawned successfully` ack. The heartbeat sidecar is an auxiliary liveness signal layered on top: a missing sidecar after the result file appears is itself a contract violation per the heartbeat rule |
387
397
  | Re-sending confirmed findings (`full-consensus`/`partial-consensus`/`worker-unique`) to a worker in Round 2 | Queue pruning rule — see [convergence](./convergence.md) "Round 1-N: Re-verification Loop (queue-pruned)" |
388
398
  | Aggregating a `timeout`/`error` reverify dispatch as `DISAGREE` | Worker failure handling — record as `verification-error` and add to `skippedWorkers[]`. See [convergence](./convergence.md) "Worker failure handling in reverify" |
389
399
  | Skipping `--substitute-data` in the Phase 7 collector run | Always pass the flag — see [report-writer](./report-writer.md) "Phase 7 token-usage collector" |
@@ -103,8 +103,6 @@ Audience-scoped file enumeration (BLOCKING — performance optimization):
103
103
 
104
104
  Asymmetry note: `claude-worker` runs in-process and the Agent SDK auto-loads its agent definition; lead's dispatch prompt body for claude-worker can therefore be shorter than for codex/antigravity. The Worker Preamble pointer is still emitted for all three so the contract source is identical regardless of dispatch path.
105
105
 
106
- Send byte-identical dispatch prompts to every analysis worker (Claude / Codex / Antigravity), modulo the role label and the wrapper-specific path headers. The prior "role-specific emphasis" guidance is retired — specialization lives in Section 6 of the worker output, not in the dispatch prompt body.
107
-
108
106
  ## Terminal Statuses
109
107
 
110
108
  Terminal statuses that can be recorded for a worker:
@@ -331,7 +329,7 @@ empty run-level error logs in production.
331
329
 
332
330
  - `cli-failure` events are recorded by the wrapper subagent itself (Codex / Antigravity), but **directly to the run-level error log** via `okstra error-log append-observed --error-type cli-failure ...` — NOT via the sidecar. The sidecar is an in-process tool-failure channel only.
333
331
  - **Wrapper invocation arity.** Both `okstra-codex-exec.sh` and `okstra-antigravity-exec.sh` accept four required positional arguments plus an optional fifth `<role>`: `<project-root> <model> <prompt-path> <worktree-path> [<role>]`. The fourth (worktree) argument is **mandatory for implementation phase** and optional otherwise. For codex it becomes `--add-dir <worktree>` (sandbox write access); for antigravity it is appended to `--include-directories`. Omitting it during implementation causes the codex sandbox to reject every Edit/Write targeting the worktree with EPERM. Workers extract the path from the `**Worktree:**` / `EXECUTOR_WORKTREE_PATH` / `cwd for every mutating command:` line in the lead prompt. The optional fifth `<role>` is folded into both the caller (worker) pane title `<cli>-<role>-<pid>` and the sibling trace-pane title `<cli>-<role>-<pid>-tail` (e.g. `codex-executor-93421` ↔ `codex-executor-93421-tail`). `<pid>` is the wrapper's own PID and disambiguates concurrent dispatches of the same role. The role value comes from the dispatch prompt's `**Pane role:**` line: `executor` on an `implementation` Executor dispatch, `verifier` on an `implementation` / `final-verification` verifier dispatch, so the trace pane names the actual job rather than a generic `worker`. When no `**Pane role:**` line is present (analysis phases), pass the literal `worker` (the wrapper also defaults to `worker` if the argument is omitted).
334
- - **Background dispatch + polling contract (Codex / Antigravity wrappers).** Both wrapper subagents MUST dispatch `okstra-codex-exec.sh` / `okstra-antigravity-exec.sh` via `Bash(run_in_background: true)` and poll with `BashOutput(bash_id)` until the shell reports `status == "completed"`, capped at 30 minutes (1800s) of wall-clock elapsed time. `BashOutput` itself is the wait primitive — call it back-to-back; do NOT insert a standalone `sleep` between polls. The Claude Code harness blocks `sleep` calls of 5 seconds or longer as a circumvention vector and explicitly forbids chaining shorter sleeps inside until-loops to work around the block. Workers that hit the contract bug must NOT self-recover with `until ...; do sleep 2; done` wrappers — that path violates the harness anti-circumvention rule, even though it superficially "works". The legacy "single foreground `Bash` with 120000ms timeout" rule, and the subsequent "60-second cadence with `sleep 60` between polls" rule, are both retired. The current rule applies in **every phase** (analysis runs typically complete in 1–2 `BashOutput` calls, so there is no regression for short jobs). Recording responsibilities:
332
+ - **Background dispatch + polling contract (Codex / Antigravity wrappers).** Both wrapper subagents MUST dispatch `okstra-codex-exec.sh` / `okstra-antigravity-exec.sh` via `Bash(run_in_background: true)` and poll with `BashOutput(bash_id)` until the shell reports `status == "completed"`, capped at 30 minutes (1800s) of wall-clock elapsed time. `BashOutput` itself is the wait primitive — call it back-to-back; do NOT insert a standalone `sleep` between polls. The Claude Code harness blocks `sleep` calls of 5 seconds or longer as a circumvention vector and explicitly forbids chaining shorter sleeps inside until-loops to work around the block. Workers that hit the contract bug must NOT self-recover with `until ...; do sleep 2; done` wrappers — that path violates the harness anti-circumvention rule, even though it superficially "works". The current rule applies in **every phase** (analysis runs typically complete in 1–2 `BashOutput` calls, so there is no regression for short jobs). Recording responsibilities:
335
333
  - Successful completion: return the wrapper's accumulated stdout from the final `BashOutput`. No log entry.
336
334
  - Non-zero `exit_code` reported by `BashOutput`: record a `cli-failure` to the run-level error log with the real `exit_code` and observed `duration-ms`.
337
335
  - Polling cap reached: before `KillShell`, perform a one-shot **mtime-grace check** on the wrapper's live log (`<prompt>.log`). If the log was written within the last 90 seconds AND grace has not yet been applied this loop, extend the cap from 1800s → 2100s (one-shot +5min) and continue polling. Otherwise (log stale, OR grace already applied), call `KillShell(shell_id)`, record `cli-failure` with `--exit-code 124 --duration-ms <observed_ms> --message "<wrapper> exceeded polling cap (grace=<applied|not-applied>, last_mtime_age=<n>s)"`, then return the language-specific `*_CLI_TIMEOUT` sentinel. The grace exists to absorb token-budget spikes where the CLI is genuinely still producing output past the 30-minute mark; it is a one-shot soft extension, NOT a loop.
@@ -371,7 +369,7 @@ Every worker result file under `worker-results/` must begin with a standardized
371
369
 
372
370
  Task-type and date are **not** repeated in this human header — they already live in the YAML frontmatter (`taskType`, `date`), which is the copy Obsidian indexes. Restating them here added a third, un-indexed, machine-unparsed copy with no value; the frontmatter is the single source for both. The `Target:` line is optional — include it when the run is scoped to a specific path or module, omit it for whole-project runs; when present, place it between the title and the `Model:` line.
373
371
 
374
- Examples:
372
+ Example (Codex / Report-writer headers are identical modulo the role name and model id):
375
373
 
376
374
  ```markdown
377
375
  # Claude Worker Analysis — jobs:tasks:8852
@@ -380,20 +378,6 @@ Examples:
380
378
  **Model:** Claude worker, opus
381
379
  ```
382
380
 
383
- ```markdown
384
- # Codex Worker Analysis — jobs:tasks:8852
385
-
386
- **Target:** server/auth.ts
387
- **Model:** Codex worker, <codex-model-id>
388
- ```
389
-
390
- ```markdown
391
- # Report Writer Worker Analysis — jobs:tasks:8852
392
-
393
- **Target:** server/auth.ts
394
- **Model:** Report writer worker, opus
395
- ```
396
-
397
381
  Use the actual model identifier recorded in team-state (never invent a model ID — read it from `resultContract.requiredWorkerRoles[*].modelExecutionValue` or the tool response metadata).
398
382
 
399
383
  The header is followed by the standard worker output contract sections (Findings, Missing Information, etc.).
@@ -22,6 +22,9 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
22
22
  - **Coding-conventions preflight (BLOCKING — runs before the first `Edit` / `Write`, and binds the TDD loop below).** The gate body is a single source at `prompts/profiles/_coding-conventions-preflight.md` (sibling of this sidecar): resolved `Coding preflight pack:` runtime-resource path usage, project review rule packs, the always-binding language-agnostic principles, and graceful degradation. Do NOT re-type that content from memory — deliver it by file so it cannot drift or be dropped:
23
23
  - **Claude executor:** Read `_coding-conventions-preflight.md` end-to-end before the first `Edit` / `Write`, then state in ONE line which conventions apply (e.g. `Applying TS + hexagonal overlay; domain at src/domains/*/domain/`).
24
24
  - **CLI executor (BLOCKING when the executor provider is `codex` or `antigravity`):** the executor CLI process does NOT share the lead's context, and it cannot Read this sidecar's directory — that path sits outside the CLI sandbox and the CLI only sees its stdin prompt, so a file reference never reaches it. The lead MUST physically append the **body** of `_coding-conventions-preflight.md` into the persisted executor prompt at dispatch time: Read the file from the same absolute directory you read this sidecar from, then `Write`/`cat` its body into the persisted prompt. Never hand-retype it. Enforcement: the CLI wrapper agents refuse an implementation-Executor dispatch whose persisted prompt lacks the literal heading `Coding-conventions preflight`, returning `<SENTINEL_PREFIX>_PREFLIGHT_MISSING` (see `agents/workers/_cli-wrapper-template.md` → Prompt Composition).
25
+ - **Completion self-check (BLOCKING — runs BEFORE you claim the stage done).** The gate body is a single source at `prompts/profiles/_implementation-self-check.md` (sibling of this sidecar): the four-item completion gate (functions ≤50 lines, conventions applied, truthful names & why-comments, real build/test run). Do NOT re-type it from memory — deliver it by file so it cannot drift.
26
+ - **Claude executor:** Read `_implementation-self-check.md` end-to-end before appending the `status:"done"` row, then write one confirming line per item to your audit sidecar.
27
+ - **CLI executor (codex / antigravity):** the CLI process cannot Read this path (outside its sandbox). The lead appends this file's body into the persisted executor prompt at dispatch time, immediately after the preflight body (see `codex_dispatch.py` `_implementation_executor_tail`). The head-less executor honours both gates from the single prompt.
25
28
  - **Stage discipline transcription (when a preceding stage is `done`):** the lead MUST transcribe the `Stage discipline` rule (from this run's rendered profile — the INCLUDEd `_stage-discipline.md` body) verbatim into the dispatched CLI executor prompt, so a codex/antigravity executor honors the prior-stage behavior-freeze. Declaration-level — no wrapper sentinel.
26
29
  - **Non-interactive auto-execution (BLOCKING when the executor provider is `codex` or `antigravity`).** A CLI executor runs head-less (`codex exec` / antigravity equivalent) — there is no human at the keyboard. Skills loaded during the run (tdd, coding-preflight, and others) contain "get user approval", "state your plan to the user and wait", or "ask before proceeding" gates written for interactive sessions; in this run those gates are **already satisfied** by the upstream `implementation-planning` approval (the plan this stage executes was human-approved). The executor MUST NOT stop to request approval, MUST NOT end its turn after only producing a plan, and MUST carry the stage through end-to-end — RED → GREEN → refactor → per-step commits → `### Stage Carry Evidence`. The ONLY skill step to skip is the interactive user-approval prompt itself; every other skill rule (TDD discipline, conventions, real-IO isolation) still binds. The lead MUST transcribe this bullet verbatim into the dispatched CLI executor prompt (same reason as the preflight transcription rule above — the CLI process does not share lead context). Stopping early for approval in a head-less run is the observed empty-exit failure (exit 0, no diff): treat it as `contract-violated`.
27
30
  - **Mandatory TDD loop**: BEFORE the first `Edit` or `Write` call, the executor MUST apply a red-green-refactor loop for every code change in this run. This is required; skipping it is a `contract-violated` outcome. This governs HOW each step is executed (failing test first → minimal implementation → refactor); it does not override the approved plan's WHAT/file scope.
@@ -0,0 +1,19 @@
1
+ <!--
2
+ Completion-time counterpart to _coding-conventions-preflight.md. The preflight
3
+ gate runs BEFORE the first Edit/Write; this gate runs BEFORE the executor claims
4
+ done. Same delivery paths: Claude executor reads this file directly; codex /
5
+ antigravity executor cannot read it (outside the CLI sandbox), so the lead
6
+ appends this file's body into the persisted executor prompt at dispatch time
7
+ (see codex_dispatch.py `_implementation_executor_tail`).
8
+ -->
9
+
10
+ # Implementation self-check (BLOCKING — before you claim done)
11
+
12
+ Before declaring the change complete, write one confirming line per item to your
13
+ audit sidecar. If any item fails, fix it or surface the violation — do not claim
14
+ done on a failing item.
15
+
16
+ - functions: every new/edited function ≤50 effective lines, single purpose
17
+ - conventions: applied the routed pack + project patterns (name which ones)
18
+ - names & comments: names say what, comments say why; no obvious-restatement comments; no truthful-name violations
19
+ - verification: ran the actual build/test (paste the command + its result); no dead or commented-out code left behind
@@ -126,3 +126,7 @@ If every verifier present in the resolved roster (`Claude verifier`, `Codex veri
126
126
  - git state mutations — `git add`, `git commit`, `git stash`, `git checkout -- <file>`, `git restore`, `git reset`, `git rebase`, `git merge`, branch creation/deletion, tag creation. Only read-only git queries (`git status`, `git diff`, `git log`, `git show`, `git rev-parse`, `git blame`) are permitted for verifiers
127
127
  - running integration / end-to-end tests that produce non-local side effects (DB writes against a non-local datastore, external API writes, docker compose against a non-isolated environment) unless that exact command is listed in the approved plan's validation set
128
128
  - redirecting tool caches or output to paths outside the worktree — e.g. setting `CARGO_TARGET_DIR`, `PYTEST_CACHE_DIR`, `NODE_OPTIONS=--require=<external>`, or any env var that causes the verifier's command to write outside the worktree's normal build artifact paths
129
+
130
+ ## Completion self-check (declaration)
131
+
132
+ - **Completion self-check (declaration).** If this verifier edits project code (not just inspects it), the same completion gate at `prompts/profiles/_implementation-self-check.md` binds before claiming done — same four items, recorded in the audit sidecar.
@@ -217,9 +217,9 @@
217
217
  "echo_template": "approved-plan: {value}"
218
218
  },
219
219
  "approve_plan_confirm": {
220
- "label": " 플랜으로 구현을 진행할까요?\n {path}\n· 예 — 이 plan 대로 구현을 실행합니다. 플랜이 아직 승인 전이면 지금 data.json(정본) + 리포트를 함께 approved 로 처리한 뒤 진행합니다. (markdown 만 손으로 고치면 일관성 검증에서 거부되므로 이 경로로 승인하세요.)\n· 아니오 — 진행하지 않습니다.",
220
+ "label": "이대로 구현을 진행할까요?\n {path}\n· 예 — 이 plan 대로 구현을 실행합니다. 플랜이 아직 승인 전이면 지금 data.json(정본) + 리포트를 함께 approved 로 처리한 뒤 진행합니다. (markdown 만 손으로 고치면 일관성 검증에서 거부되므로 이 경로로 승인하세요.)\n· 아니오 — 진행하지 않습니다.",
221
221
  "label_final_verification": "이 plan 을 검증 기준으로 삼아 진행할까요?\n {path}\n· 예 — 선택한 stage 의 구현 결과를 위 plan 기준으로 검증합니다. (plan 이 아직 승인 전이면 data.json(정본) + 리포트를 함께 approved 로 처리한 뒤 검증합니다.)\n· 아니오 — 진행하지 않습니다.",
222
- "html_approval_note": "\n보고서에서 내보낸 승인 기록을 발견했습니다: {sidecar}\n 선택한 구현 옵션: {option}\n· 예(기록 적용) — 승인과 함께 위 옵션을 frontmatter 에 기록합니다.",
222
+ "html_approval_note": "\n지난 {plan_label} 의 보고서에서 사용자의 승인 기록을 발견했습니다.\n 선택된 방식: {option}",
223
223
  "html_approval_note_default_option": "(추천 옵션 그대로)",
224
224
  "echo_template": "approve-plan: {value}",
225
225
  "options": {
@@ -231,8 +231,8 @@
231
231
  "no": "아니오 — 진행하지 않음"
232
232
  },
233
233
  "options_html_approval": {
234
- "yes_apply": "예 — 내보낸 기록대로 승인 + 옵션 적용 (추천)",
235
- "yes": "예 — 승인만 (내보낸 기록 무시)",
234
+ "yes_apply": "예 — 지난 {plan_label} 보고서, 사용자의 승인 기록으로 진행 (추천)",
235
+ "yes": "예 — 내보낸 기록 무시 후 진행 — 진행 방식은 구현 중 다시 정함",
236
236
  "no": "아니오 — 진행하지 않음"
237
237
  },
238
238
  "echo_variants": {
@@ -806,10 +806,17 @@ def _implementation_executor_tail(
806
806
  ) -> str:
807
807
  if role != "executor" or _require_string(manifest, "taskType") != "implementation":
808
808
  return ""
809
- preflight_path = workspace_root / "prompts" / "profiles" / "_coding-conventions-preflight.md"
810
- if not preflight_path.is_file():
811
- return "# Coding-conventions preflight\n\nApply project-local conventions before editing."
812
- return preflight_path.read_text(encoding="utf-8")
809
+ profiles = workspace_root / "prompts" / "profiles"
810
+ parts: list[str] = []
811
+ preflight_path = profiles / "_coding-conventions-preflight.md"
812
+ if preflight_path.is_file():
813
+ parts.append(preflight_path.read_text(encoding="utf-8"))
814
+ else:
815
+ parts.append("# Coding-conventions preflight\n\nApply project-local conventions before editing.")
816
+ selfcheck_path = profiles / "_implementation-self-check.md"
817
+ if selfcheck_path.is_file():
818
+ parts.append(selfcheck_path.read_text(encoding="utf-8"))
819
+ return "\n\n".join(parts)
813
820
 
814
821
 
815
822
  def _project_relative_path(project_root: Path, path: Path) -> str:
@@ -628,6 +628,14 @@ def _validate_sidecar_option(plan_path: Path, option_name: str, errors_t: dict)
628
628
  )
629
629
 
630
630
 
631
+ def _plan_short_label(candidate: str) -> str:
632
+ """plan 파일명에서 사용자용 짧은 식별자를 뽑는다.
633
+ final-report-implementation-planning-002.md → implementation-planning-002"""
634
+ if not candidate:
635
+ return ""
636
+ return Path(candidate).stem.removeprefix("final-report-")
637
+
638
+
631
639
  def _stage_plan_for_confirmation(
632
640
  state: WizardState, path_str: str, *, suffix: str = ""
633
641
  ) -> Optional[str]:
@@ -1713,12 +1721,16 @@ def _build_approve_plan_confirm(state: WizardState) -> Prompt:
1713
1721
  label = (t["label_final_verification"] or t["label"]) if is_fv else t["label"]
1714
1722
  options_map = (t["options_final_verification"] or t["options"]) if is_fv else t["options"]
1715
1723
  if state.html_approval_sidecar:
1724
+ plan_label = _plan_short_label(state.approve_plan_candidate)
1716
1725
  label += t["html_approval_note"].format(
1717
- sidecar=state.html_approval_sidecar,
1726
+ plan_label=plan_label,
1718
1727
  option=state.html_approval_option
1719
1728
  or t["html_approval_note_default_option"],
1720
1729
  )
1721
- options_map = t["options_html_approval"]
1730
+ options_map = {
1731
+ k: v.format(plan_label=plan_label)
1732
+ for k, v in t["options_html_approval"].items()
1733
+ }
1722
1734
  return Prompt(
1723
1735
  step=S_APPROVE_PLAN_CONFIRM, kind="pick",
1724
1736
  label=label,
@@ -0,0 +1,22 @@
1
+ # Operating Standard (canonical)
2
+
3
+ This file is the single source of truth for the operating standard injected into
4
+ every okstra agent. The "Common core" block below is inlined verbatim into
5
+ `templates/worker-prompt-preamble.md` (workers) and
6
+ `prompts/lead/okstra-lead-contract.md` (lead). A unit test asserts both inlined
7
+ copies match this core — update all three together.
8
+
9
+ ## Common core
10
+
11
+ Work like a senior engineer who owns this result, not a commentator on it.
12
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
13
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
14
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
15
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
16
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
17
+
18
+ ## Role addendum
19
+
20
+ - Analysis / verification workers add this line: `Hold your own line — reason from your independent angle; do not drift toward the other workers' likely answers or echo the user. Triangulation fails if your view isn't genuinely yours.`
21
+ - Lead adds this line: `Own the synthesis — weigh worker outputs on evidence, not consensus; a better-grounded dissent outranks the majority.`
22
+ - Report-writer worker: common core only (no independence addendum — it authors, it does not analyze).
@@ -4,6 +4,16 @@ This file is the single source of truth for the boilerplate that every okstra an
4
4
 
5
5
  It replaces the previous practice of inlining ~80 lines of identical boilerplate into every worker prompt body.
6
6
 
7
+ ## Operating standard (read before any output)
8
+
9
+ Work like a senior engineer who owns this result, not a commentator on it.
10
+ - Evidence over assertion — back every claim with a file:line, or mark it an explicit assumption. Never state the unverified as fact.
11
+ - Read before you reason — read each required input end to end; when you lack basis, write "insufficient evidence" instead of filling the gap plausibly.
12
+ - Shortest sound path — chase the most likely cause first; don't re-verify what is settled or pad with restatement.
13
+ - Decide, don't survey — when options exist, give the trade-off and one recommendation, not an exhaustive list.
14
+ - Fit what's here — match the surrounding code and prose; size the response to the request.
15
+ - Hold your own line — reason from your independent angle; do not drift toward the other workers' likely answers or echo the user. Triangulation fails if your view isn't genuinely yours.
16
+
7
17
  ---
8
18
 
9
19
  ## Required reading (analysis workers + report-writer worker)