okstra 0.102.2 → 0.102.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.kr.md +5 -5
- package/README.md +5 -5
- package/docs/kr/architecture/storage-model.md +1 -1
- package/docs/kr/architecture.md +7 -11
- package/docs/kr/cli.md +4 -4
- package/docs/kr/container.md +1 -1
- package/docs/kr/performance-improvement-plan-v2.md +2 -2
- package/docs/project-structure-overview.md +2 -2
- package/package.json +7 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/okstra-antigravity-exec.sh +1 -2
- package/runtime/bin/okstra-codex-exec.sh +1 -2
- package/runtime/bin/okstra-render-final-report.py +2 -9
- package/runtime/bin/okstra-spawn-followups.py +4 -6
- package/runtime/bin/okstra-trace-cleanup.sh +24 -4
- package/runtime/prompts/lead/okstra-lead-contract.md +2 -2
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +9 -11
- package/runtime/python/okstra_ctl/codex_dispatch.py +4 -12
- package/runtime/python/okstra_ctl/consumers.py +35 -0
- package/runtime/python/okstra_ctl/container.py +4 -6
- package/runtime/python/okstra_ctl/dispatch_core.py +4 -12
- package/runtime/python/okstra_ctl/final_report_paths.py +24 -0
- package/runtime/python/okstra_ctl/handoff.py +2 -1
- package/runtime/python/okstra_ctl/implementation_stage.py +7 -11
- package/runtime/python/okstra_ctl/report_view_artifacts.py +14 -0
- package/runtime/python/okstra_ctl/report_views.py +7 -4
- package/runtime/python/okstra_ctl/run.py +3 -10
- package/runtime/python/okstra_ctl/stage_targets.py +18 -0
- package/runtime/python/okstra_ctl/wizard.py +24 -25
- package/runtime/python/okstra_ctl/wizard_stage_intent.py +54 -0
- package/runtime/python/okstra_token_usage/report.py +2 -5
- package/runtime/validators/validate-report-views.py +2 -1
- package/runtime/validators/validate-run.py +1 -11
- package/src/commands/lifecycle/install.mjs +5 -5
- package/src/commands/lifecycle/uninstall.mjs +4 -4
- package/docs/superpowers/plans/2026-05-12-ticket-id-in-reports.md +0 -638
- package/docs/superpowers/plans/2026-05-14-convergence-queue-pruning.md +0 -1568
- package/docs/superpowers/plans/2026-05-17-dual-format-final-report.md +0 -167
- package/docs/superpowers/plans/2026-05-20-final-report-language.md +0 -1501
- package/docs/superpowers/plans/2026-05-20-implementation-planning-multi-stage.md +0 -1267
- package/docs/superpowers/plans/2026-05-20-okstra-run-prompt-sot-b1.md +0 -1007
- package/docs/superpowers/plans/2026-05-20-wizard-messages-json-sot.md +0 -720
- package/docs/superpowers/plans/2026-05-20-wizard-prompt-json-sot-a1.md +0 -681
- package/docs/superpowers/plans/2026-05-21-improvement-discovery-task-type.md +0 -1691
- package/docs/superpowers/plans/2026-05-24-implementation-lead-context-slimming.md +0 -1700
- package/docs/superpowers/plans/2026-05-25-okstra-project-root-rename.md +0 -158
- package/docs/superpowers/plans/2026-05-26-wizard-3-option-picker.md +0 -860
- package/docs/superpowers/plans/2026-06-02-final-verification-protocol-hardening.md +0 -326
- package/docs/superpowers/plans/2026-06-02-okstra-run-branch-confirm-step.md +0 -337
- package/docs/superpowers/plans/2026-06-02-okstra-run-phase-pane-cleanup.md +0 -410
- package/docs/superpowers/plans/2026-06-02-requirements-discovery-fanout.md +0 -728
- package/docs/superpowers/plans/2026-06-04-adversarial-implementation-planning.md +0 -294
- package/docs/superpowers/plans/2026-06-04-adversarial-verification.md +0 -570
- package/docs/superpowers/plans/2026-06-04-coverage-critic.md +0 -516
- package/docs/superpowers/plans/2026-06-04-stage-cohesion-planner.md +0 -351
- package/docs/superpowers/plans/2026-06-04-stage-run-batching.md +0 -457
- package/docs/superpowers/plans/2026-06-05-acceptance-critic.md +0 -251
- package/docs/superpowers/plans/2026-06-05-compact-markdown-report-tables.md +0 -323
- package/docs/superpowers/plans/2026-06-05-wizard-batch-prompts.md +0 -559
- package/docs/superpowers/plans/2026-06-06-final-verification-whole-task-gate.md +0 -993
- package/docs/superpowers/plans/2026-06-06-stage-parallel-and-pending-fixes.md +0 -93
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p1.md +0 -447
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p2.md +0 -289
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p3.md +0 -774
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p4.md +0 -303
- package/docs/superpowers/plans/2026-06-06-stage-worktree-isolation-p5-multidep-base.md +0 -387
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase2.md +0 -275
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase3.md +0 -282
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4a.md +0 -147
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4b.md +0 -262
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4c.md +0 -184
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4d.md +0 -88
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa-phase4e.md +0 -250
- package/docs/superpowers/plans/2026-06-07-stage-conformance-qa.md +0 -409
- package/docs/superpowers/plans/2026-06-09-implementation-run-artifact-stage-isolation.md +0 -320
- package/docs/superpowers/plans/2026-06-10-concurrent-run-team-guard.md +0 -456
- package/docs/superpowers/plans/2026-06-10-git-reconcile-stale-sha-recovery.md +0 -1408
- package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling-PROBE.md +0 -42
- package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling.md +0 -337
- package/docs/superpowers/plans/2026-06-10-p6-token-usage-incremental.md +0 -1029
- package/docs/superpowers/plans/2026-06-10-stage-group-handoff.md +0 -1572
- package/docs/superpowers/plans/2026-06-11-fix-cycle.md +0 -1290
- package/docs/superpowers/plans/2026-06-11-wizard-whole-task-final-verification.md +0 -526
- package/docs/superpowers/plans/2026-06-12-html-plan-approval.md +0 -1000
- package/docs/superpowers/plans/2026-06-13-repo-risk-hardening.md +0 -493
- package/docs/superpowers/plans/2026-06-14-host-runtime-auto-execution.md +0 -1775
- package/docs/superpowers/plans/2026-06-15-coding-preflight-pack-dispatch-path.md +0 -504
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-final-fixups.md +0 -342
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-fixups.md +0 -258
- package/docs/superpowers/plans/2026-06-15-internal-skill-migration-remaining-fixups.md +0 -387
- package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +0 -749
- package/docs/superpowers/plans/2026-06-15-worker-prompt-anchor-final-fixups.md +0 -828
- package/docs/superpowers/plans/2026-06-15-worker-prompt-header-error-contract.md +0 -490
- package/docs/superpowers/plans/2026-06-17-okstra-error-report.md +0 -724
- package/docs/superpowers/plans/2026-06-18-subagent-pane-reclaim.md +0 -483
- package/docs/superpowers/plans/2026-06-19-agent-operating-standard.md +0 -476
- package/docs/superpowers/plans/2026-06-19-okstra-inspect-recap-facet.md +0 -647
- package/docs/superpowers/plans/2026-06-20-error-feedback-loop.md +0 -1019
- package/docs/superpowers/plans/2026-06-20-run-index-row-normalization.md +0 -583
- package/docs/superpowers/plans/2026-06-20-stage-auto-integrate-teardown.md +0 -819
- package/docs/superpowers/plans/2026-06-21-okstra-container-local-user-test.md +0 -714
- package/docs/superpowers/plans/2026-06-21-release-handoff-local-checkout.md +0 -348
- package/docs/superpowers/plans/2026-06-23-implementation-stage-multiselect-chain.md +0 -630
- package/docs/superpowers/plans/2026-06-23-implementation-step-commit-granularity.md +0 -274
- package/docs/superpowers/plans/2026-06-23-phase-batch-cleanup-enforcement.md +0 -409
- package/docs/superpowers/plans/2026-06-24-cross-project-precondition.md +0 -542
- package/docs/superpowers/plans/2026-06-24-decision-drafts-render-gap.md +0 -568
- package/docs/superpowers/plans/2026-06-24-manual-user-test-section.md +0 -203
- package/docs/superpowers/plans/2026-06-24-okstra-container-build-rename.md +0 -525
- package/docs/superpowers/specs/2026-05-12-ticket-id-in-reports-design.md +0 -131
- package/docs/superpowers/specs/2026-05-15-implementation-plan-verification-design.md +0 -254
- package/docs/superpowers/specs/2026-05-20-final-report-language-design.md +0 -383
- package/docs/superpowers/specs/2026-05-20-implementation-planning-multi-stage-design.md +0 -322
- package/docs/superpowers/specs/2026-05-20-okstra-run-prompt-sot-design.md +0 -299
- package/docs/superpowers/specs/2026-05-21-improvement-discovery-task-type-design.md +0 -335
- package/docs/superpowers/specs/2026-06-02-okstra-run-branch-confirm-step-design.md +0 -113
- package/docs/superpowers/specs/2026-06-02-okstra-run-phase-pane-cleanup-design.md +0 -173
- package/docs/superpowers/specs/2026-06-02-requirements-discovery-fanout-design.md +0 -154
- package/docs/superpowers/specs/2026-06-04-adversarial-implementation-planning-design.md +0 -90
- package/docs/superpowers/specs/2026-06-04-adversarial-verification-design.md +0 -176
- package/docs/superpowers/specs/2026-06-04-coverage-critic-design.md +0 -99
- package/docs/superpowers/specs/2026-06-04-stage-splitting-cost-aware-design.md +0 -98
- package/docs/superpowers/specs/2026-06-05-acceptance-critic-design.md +0 -90
- package/docs/superpowers/specs/2026-06-05-compact-markdown-report-tables-design.md +0 -87
- package/docs/superpowers/specs/2026-06-05-wizard-batch-prompts-design.md +0 -121
- package/docs/superpowers/specs/2026-06-06-final-verification-whole-task-gate-design.md +0 -126
- package/docs/superpowers/specs/2026-06-06-stage-worktree-isolation-design.md +0 -180
- package/docs/superpowers/specs/2026-06-06-vertical-slice-tdd-planning-design.md +0 -179
- package/docs/superpowers/specs/2026-06-07-stage-conformance-qa-design.md +0 -169
- package/docs/superpowers/specs/2026-06-09-executor-model-custom-id-cascade-design.md +0 -66
- package/docs/superpowers/specs/2026-06-09-implementation-run-artifact-stage-isolation-design.md +0 -87
- package/docs/superpowers/specs/2026-06-10-blocking-contract-posthoc-conformance-design.md +0 -168
- package/docs/superpowers/specs/2026-06-10-concurrent-run-team-guard-design.md +0 -107
- package/docs/superpowers/specs/2026-06-10-git-reconcile-stale-sha-recovery-design.md +0 -105
- package/docs/superpowers/specs/2026-06-10-lead-worker-completion-polling-design.md +0 -113
- package/docs/superpowers/specs/2026-06-10-stage-group-handoff-design.md +0 -156
- package/docs/superpowers/specs/2026-06-11-brief-entry-only-handoff-stage-entry-design.md +0 -158
- package/docs/superpowers/specs/2026-06-11-fix-cycle-design.md +0 -94
- package/docs/superpowers/specs/2026-06-11-wizard-whole-task-final-verification-design.md +0 -89
- package/docs/superpowers/specs/2026-06-12-codex-lead-adapter-design.md +0 -358
- package/docs/superpowers/specs/2026-06-12-html-plan-approval-design.md +0 -85
- package/docs/superpowers/specs/2026-06-12-stage-discipline-rule-design.md +0 -108
- package/docs/superpowers/specs/2026-06-13-forbidden-actions-ssot-design.md +0 -134
- package/docs/superpowers/specs/2026-06-13-neutral-tmux-lead-adapter-design.md +0 -284
- package/docs/superpowers/specs/2026-06-14-host-runtime-auto-execution-design.md +0 -404
- package/docs/superpowers/specs/2026-06-17-okstra-error-report-design.md +0 -123
- package/docs/superpowers/specs/2026-06-18-subagent-pane-reclaim-design.md +0 -150
- package/docs/superpowers/specs/2026-06-19-agent-operating-standard-design.md +0 -120
- package/docs/superpowers/specs/2026-06-19-okstra-inspect-recap-facet-design.md +0 -96
- package/docs/superpowers/specs/2026-06-20-error-feedback-loop-design.md +0 -171
- package/docs/superpowers/specs/2026-06-20-run-index-row-normalization-design.md +0 -112
- package/docs/superpowers/specs/2026-06-20-stage-auto-integrate-teardown-design.md +0 -145
- package/docs/superpowers/specs/2026-06-21-okstra-container-local-user-test-design.md +0 -125
- package/docs/superpowers/specs/2026-06-21-release-handoff-local-checkout-design.md +0 -113
- package/docs/superpowers/specs/2026-06-23-implementation-stage-multiselect-chain-design.md +0 -138
- package/docs/superpowers/specs/2026-06-23-implementation-step-commit-granularity-design.md +0 -120
- package/docs/superpowers/specs/2026-06-24-cross-project-precondition-design.md +0 -129
- package/docs/superpowers/specs/2026-06-24-decision-drafts-render-gap-design.md +0 -185
- package/docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md +0 -121
- package/docs/superpowers/specs/2026-06-24-okstra-container-build-rename-design.md +0 -110
|
@@ -1,476 +0,0 @@
|
|
|
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)` 는 현재 코드와 일치.
|