okstra 0.101.0 → 0.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/kr/architecture.md +1 -1
- package/docs/project-structure-overview.md +1 -1
- package/docs/superpowers/plans/2026-06-24-cross-project-precondition.md +542 -0
- package/docs/superpowers/plans/2026-06-24-decision-drafts-render-gap.md +568 -0
- package/docs/superpowers/plans/2026-06-24-manual-user-test-section.md +203 -0
- package/docs/superpowers/specs/2026-06-24-cross-project-precondition-design.md +129 -0
- package/docs/superpowers/specs/2026-06-24-decision-drafts-render-gap-design.md +185 -0
- package/docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md +121 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +1 -0
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -0
- package/runtime/prompts/profiles/final-verification.md +4 -3
- package/runtime/prompts/profiles/implementation-planning.md +12 -9
- package/runtime/python/okstra_ctl/report_views.py +39 -3
- package/runtime/python/okstra_ctl/run.py +0 -1
- package/runtime/schemas/final-report-v1.0.schema.json +106 -3
- package/runtime/skills/okstra-container/SKILL.md +1 -1
- package/runtime/templates/reports/final-report.template.md +63 -2
- package/runtime/templates/reports/final-verification-input.template.md +3 -3
- package/runtime/templates/reports/i18n/en.json +28 -1
- package/runtime/templates/reports/i18n/ko.json +28 -1
- package/runtime/templates/reports/report.css +10 -0
- package/runtime/templates/reports/report.js +16 -0
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-run.py +70 -1
|
@@ -217,6 +217,22 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
217
217
|
{% endfor %}
|
|
218
218
|
{%- endif %}
|
|
219
219
|
|
|
220
|
+
### Cross-Project Dependencies{% if t("sectionAside.crossProjectDependencies") != "Cross-Project Dependencies" %} ({{ t("sectionAside.crossProjectDependencies") }}){% endif %}
|
|
221
|
+
|
|
222
|
+
{% if implementationPlanning.crossProjectDependencies | length == 0 -%}
|
|
223
|
+
{{ t("emptyState.crossProjectDependencies") }}
|
|
224
|
+
{%- else %}
|
|
225
|
+
{% for xp in implementationPlanning.crossProjectDependencies %}
|
|
226
|
+
**{{ xp.id }} — [{{ xp.direction }}] {{ xp.project }}**
|
|
227
|
+
|
|
228
|
+
- {{ t("implementationPlanning.crossProjectFields.requiredWork") }}: {{ xp.requiredWork }}
|
|
229
|
+
- {{ t("implementationPlanning.crossProjectFields.verificationSignal") }}: {{ xp.verificationSignal }}
|
|
230
|
+
- {{ t("implementationPlanning.crossProjectFields.linkedWork") }}: {{ xp.linkedWork }}
|
|
231
|
+
- {{ t("implementationPlanning.crossProjectFields.howToStart") }}: {{ xp.howToStart }}
|
|
232
|
+
{% endfor %}
|
|
233
|
+
{{ t("implementationPlanning.crossProjectRecognitionNote") }}
|
|
234
|
+
{%- endif %}
|
|
235
|
+
|
|
220
236
|
### Validation Checklist{% if t("sectionAside.validationChecklist") != "Validation Checklist" %} ({{ t("sectionAside.validationChecklist") }}){% endif %}
|
|
221
237
|
|
|
222
238
|
| ID | Phase | Ticket ID | Check | Command / Observation | Expected outcome |
|
|
@@ -241,6 +257,25 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
241
257
|
| {{ row.id | mdcell }} | `{{ row.source | mdcell }}` | {{ row.requirement | mdcell }} | {{ row.coveredBy | mdcell }} | `{{ row.status | mdcell }}` |
|
|
242
258
|
{% endfor %}
|
|
243
259
|
|
|
260
|
+
### Decision Drafts{% if t("sectionAside.decisionDrafts") != "Decision Drafts" %} ({{ t("sectionAside.decisionDrafts") }}){% endif %}
|
|
261
|
+
|
|
262
|
+
{% if (implementationPlanning.decisionDrafts | length == 0) and (implementationPlanning.skippedAdrCandidates | length == 0) -%}
|
|
263
|
+
{{ t("emptyState.decisionDrafts") }}
|
|
264
|
+
{%- else %}
|
|
265
|
+
{% for d in implementationPlanning.decisionDrafts %}
|
|
266
|
+
#### {{ d.number }}-{{ d.slug }}
|
|
267
|
+
|
|
268
|
+
- **{{ t("implementationPlanning.decisionDraftFields.status") }}:** {{ d.status }}
|
|
269
|
+
- **{{ t("implementationPlanning.decisionDraftFields.context") }}:** {{ d.context }}
|
|
270
|
+
- **{{ t("implementationPlanning.decisionDraftFields.decision") }}:** {{ d.decision }}
|
|
271
|
+
- **{{ t("implementationPlanning.decisionDraftFields.consequences") }}:** {{ d.consequences }}
|
|
272
|
+
- **{{ t("implementationPlanning.decisionDraftFields.alternativesConsidered") }}:** {{ d.alternativesConsidered }}
|
|
273
|
+
{% endfor %}
|
|
274
|
+
{% for s in implementationPlanning.skippedAdrCandidates %}
|
|
275
|
+
- skipped adr-candidate: {{ s.topic }} — reason: {{ s.reason }}
|
|
276
|
+
{% endfor %}
|
|
277
|
+
{%- endif %}
|
|
278
|
+
|
|
244
279
|
## 5.5 Stage Map
|
|
245
280
|
|
|
246
281
|
| stage | title | depends-on | step-count | exit-contract-summary |
|
|
@@ -482,7 +517,21 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
482
517
|
| {{ row.category | mdcell }} | `{{ row.rollbackCommand | mdcell }}` | {{ row.verification | mdcell }} | `{{ row.result | mdcell }}` |
|
|
483
518
|
{% endfor %}
|
|
484
519
|
|
|
485
|
-
### 5.7.9
|
|
520
|
+
### 5.7.9 Manual User Test (Draft)
|
|
521
|
+
|
|
522
|
+
{% if implementation.manualUserTest.applicable -%}
|
|
523
|
+
{% for item in implementation.manualUserTest.items %}
|
|
524
|
+
- **{{ item.target }}**
|
|
525
|
+
- {{ t("manualUserTest.environmentSetup") }}: {{ item.environmentSetup }}
|
|
526
|
+
- {{ t("manualUserTest.steps") }}:
|
|
527
|
+
{% for step in item.steps %} {{ loop.index }}. {{ step }}
|
|
528
|
+
{% endfor %} - {{ t("manualUserTest.expectedResult") }}: {{ item.expectedResult }}
|
|
529
|
+
{% endfor %}
|
|
530
|
+
{%- else -%}
|
|
531
|
+
- {{ t("manualUserTest.notApplicable") }}: {{ implementation.manualUserTest.exemptionReason }}
|
|
532
|
+
{%- endif %}
|
|
533
|
+
|
|
534
|
+
### 5.7.10 Routing Recommendation
|
|
486
535
|
|
|
487
536
|
{{ implementation.routingRecommendation }}
|
|
488
537
|
|
|
@@ -565,7 +614,19 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
|
|
|
565
614
|
{% endfor %}
|
|
566
615
|
{%- endif %}
|
|
567
616
|
|
|
568
|
-
### 5.8.7
|
|
617
|
+
### 5.8.7 Manual User Test Results
|
|
618
|
+
|
|
619
|
+
{% if finalVerification.manualUserTest.applicable -%}
|
|
620
|
+
| {{ t("manualUserTest.performed") }} | {{ t("manualUserTest.result") }} | {{ t("manualUserTest.observed") }} |
|
|
621
|
+
|---|---|---|
|
|
622
|
+
{% for row in finalVerification.manualUserTest.results -%}
|
|
623
|
+
| {{ row.target | mdcell }} — {{ row.performed | mdcell }} | `{{ row.result | mdcell }}` | {{ row.observed | mdcell }} |
|
|
624
|
+
{% endfor %}
|
|
625
|
+
{%- else -%}
|
|
626
|
+
- {{ t("manualUserTest.notApplicable") }}: {{ finalVerification.manualUserTest.exemptionReaffirm }}
|
|
627
|
+
{%- endif %}
|
|
628
|
+
|
|
629
|
+
### 5.8.8 Routing Recommendation
|
|
569
630
|
|
|
570
631
|
{{ finalVerification.routingRecommendation }}
|
|
571
632
|
|
|
@@ -40,7 +40,7 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
40
40
|
- Path (project-relative) to the originating `implementation` final-report:
|
|
41
41
|
- Quoted `Commit list` / `Diff summary` excerpt from the implementation report:
|
|
42
42
|
|
|
43
|
-
> 보고서 경로가 비거나 누락된 보고서를 가리키면 final-verification 은 status `blocked` 으로 끝내고 `implementation` 또는 `implementation-planning` 으로 라우팅합니다. 검증 대상(worktree/base
|
|
43
|
+
> 보고서 경로가 비거나 누락된 보고서를 가리키면 final-verification 은 status `blocked` 으로 끝내고 `implementation` 또는 `implementation-planning` 으로 라우팅합니다. 검증 대상(worktree/base)은 okstra 가 자동 해소하므로 수동 기입이 어긋나 막히는 일은 없습니다.
|
|
44
44
|
|
|
45
45
|
## Requirement Coverage Source
|
|
46
46
|
|
|
@@ -55,7 +55,7 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
55
55
|
|
|
56
56
|
- PR or change summary:
|
|
57
57
|
- Test results:
|
|
58
|
-
- Manual verification notes:
|
|
58
|
+
- Manual verification notes (execute the source implementation report's §5.7.9 Manual User Test Draft and record results here):
|
|
59
59
|
- Related code paths:
|
|
60
60
|
- Release or rollout notes:
|
|
61
61
|
- Previous reports in the same task history:
|
|
@@ -90,7 +90,7 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
90
90
|
|
|
91
91
|
## Questions for Analysers
|
|
92
92
|
|
|
93
|
-
1. Did your analysis run against the injected `VERIFICATION_TARGET` (base
|
|
93
|
+
1. Did your analysis run against the injected `VERIFICATION_TARGET` (base ref / worktree), and does the worktree's delivered diff fully cover the stage(s) under verification? (A worktree you cannot confirm against the injected target is a `tool-failure`, not a silent proceed.)
|
|
94
94
|
2. For each requirement / acceptance criterion, what exact artifact (commit SHA, test output, log line, config value) proves coverage?
|
|
95
95
|
3. Are there any acceptance blockers?
|
|
96
96
|
4. What residual risks remain?
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"primaryEvidence": "- No primary evidence.",
|
|
10
10
|
"secondaryEvidence": "- No secondary evidence or alternate interpretations.",
|
|
11
11
|
"risks": "- No missing information or risks.",
|
|
12
|
+
"crossProjectDependencies": "- No upstream/downstream dependency on another project.",
|
|
13
|
+
"decisionDrafts": "- No decision drafts or skipped ADR candidates.",
|
|
12
14
|
"dependencyRisk": "- No dependency / migration risks.",
|
|
13
15
|
"dissent": "- No dissenting opinions.",
|
|
14
16
|
"outOfPlanEdits": "- No out-of-plan edits.",
|
|
@@ -29,6 +31,8 @@
|
|
|
29
31
|
"checkMethod": "How to check"
|
|
30
32
|
},
|
|
31
33
|
"sectionAside": {
|
|
34
|
+
"crossProjectDependencies": "Cross-Project Dependencies",
|
|
35
|
+
"decisionDrafts": "Decision Drafts",
|
|
32
36
|
"dependencyRisk": "Dependency / Migration Risk",
|
|
33
37
|
"validationChecklist": "Validation Checklist",
|
|
34
38
|
"rollbackStrategy": "Rollback Strategy",
|
|
@@ -95,7 +99,21 @@
|
|
|
95
99
|
"rationaleLabel": "Rationale (trade-off row / principle)",
|
|
96
100
|
"rejectedSummaryLabel": "Summary of rejected options",
|
|
97
101
|
"columnImpact": "Impact",
|
|
98
|
-
"columnMitigation": "Mitigation / prerequisite"
|
|
102
|
+
"columnMitigation": "Mitigation / prerequisite",
|
|
103
|
+
"crossProjectFields": {
|
|
104
|
+
"requiredWork": "Required work",
|
|
105
|
+
"verificationSignal": "Verification signal",
|
|
106
|
+
"linkedWork": "Linked work",
|
|
107
|
+
"howToStart": "How to start"
|
|
108
|
+
},
|
|
109
|
+
"crossProjectRecognitionNote": "Note: the other project's run is structurally independent — its done-set, stage numbers, and depends-on all resolve against its own repo, and this run's completed stages are NOT auto-recognized as done there. This section is narrative input that seeds the other run's planning only.",
|
|
110
|
+
"decisionDraftFields": {
|
|
111
|
+
"status": "Status",
|
|
112
|
+
"context": "Context",
|
|
113
|
+
"decision": "Decision",
|
|
114
|
+
"consequences": "Consequences",
|
|
115
|
+
"alternativesConsidered": "Alternatives Considered"
|
|
116
|
+
}
|
|
99
117
|
},
|
|
100
118
|
"releaseHandoff": {
|
|
101
119
|
"auditNote": "Audit trail for the phase that ran git/gh mutating commands.",
|
|
@@ -145,6 +163,15 @@
|
|
|
145
163
|
"verificationScope": "Verification scope",
|
|
146
164
|
"stageReportsLabel": "Source implementation reports (per stage)"
|
|
147
165
|
},
|
|
166
|
+
"manualUserTest": {
|
|
167
|
+
"environmentSetup": "Environment",
|
|
168
|
+
"steps": "Steps",
|
|
169
|
+
"expectedResult": "Expected result",
|
|
170
|
+
"notApplicable": "Manual user test not required",
|
|
171
|
+
"performed": "Performed",
|
|
172
|
+
"result": "Result",
|
|
173
|
+
"observed": "Observed"
|
|
174
|
+
},
|
|
148
175
|
"index": {
|
|
149
176
|
"heading": "Index",
|
|
150
177
|
"sectionsLabel": "Sections",
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
"primaryEvidence": "- 주 증거 없음.",
|
|
10
10
|
"secondaryEvidence": "- 보조 증거 또는 대안 해석 없음.",
|
|
11
11
|
"risks": "- 누락된 정보·위험 없음.",
|
|
12
|
+
"crossProjectDependencies": "- 다른 프로젝트에 대한 선행/후속 의존 없음.",
|
|
13
|
+
"decisionDrafts": "- 결정 기록 초안이나 스킵된 ADR 후보 없음.",
|
|
12
14
|
"dependencyRisk": "- 의존성·마이그레이션 위험 없음.",
|
|
13
15
|
"dissent": "- 반대 의견 없음.",
|
|
14
16
|
"outOfPlanEdits": "- 계획 외 편집 없음.",
|
|
@@ -29,6 +31,8 @@
|
|
|
29
31
|
"checkMethod": "확인 방법"
|
|
30
32
|
},
|
|
31
33
|
"sectionAside": {
|
|
34
|
+
"crossProjectDependencies": "프로젝트 간 의존성",
|
|
35
|
+
"decisionDrafts": "결정 기록 초안",
|
|
32
36
|
"dependencyRisk": "의존성·마이그레이션 위험",
|
|
33
37
|
"validationChecklist": "검증 체크리스트",
|
|
34
38
|
"rollbackStrategy": "롤백 전략",
|
|
@@ -95,7 +99,21 @@
|
|
|
95
99
|
"rationaleLabel": "근거 (Trade-off 행 / 원칙)",
|
|
96
100
|
"rejectedSummaryLabel": "채택되지 않은 옵션 요약",
|
|
97
101
|
"columnImpact": "영향",
|
|
98
|
-
"columnMitigation": "완화 / 선행 작업"
|
|
102
|
+
"columnMitigation": "완화 / 선행 작업",
|
|
103
|
+
"crossProjectFields": {
|
|
104
|
+
"requiredWork": "필요 작업",
|
|
105
|
+
"verificationSignal": "검증 신호",
|
|
106
|
+
"linkedWork": "연결된 작업",
|
|
107
|
+
"howToStart": "시작 방법"
|
|
108
|
+
},
|
|
109
|
+
"crossProjectRecognitionNote": "참고: 상대 프로젝트의 run 은 구조적으로 독립이다 — done-set·stage 번호·depends-on 이 모두 자기 repo 기준이며, 이 run 의 완료 stage 가 자동으로 done 으로 인식되지 않는다. 이 섹션은 상대 run 의 planning 을 seed 하는 narrative 입력일 뿐이다.",
|
|
110
|
+
"decisionDraftFields": {
|
|
111
|
+
"status": "상태",
|
|
112
|
+
"context": "맥락",
|
|
113
|
+
"decision": "결정",
|
|
114
|
+
"consequences": "결과",
|
|
115
|
+
"alternativesConsidered": "검토한 대안"
|
|
116
|
+
}
|
|
99
117
|
},
|
|
100
118
|
"releaseHandoff": {
|
|
101
119
|
"auditNote": "git/gh mutating 명령이 실행된 phase 의 감사 기록.",
|
|
@@ -145,6 +163,15 @@
|
|
|
145
163
|
"verificationScope": "검증 범위",
|
|
146
164
|
"stageReportsLabel": "stage 별 구현 리포트"
|
|
147
165
|
},
|
|
166
|
+
"manualUserTest": {
|
|
167
|
+
"environmentSetup": "환경 기동",
|
|
168
|
+
"steps": "확인 스텝",
|
|
169
|
+
"expectedResult": "기대 결과",
|
|
170
|
+
"notApplicable": "사용자 테스트 불필요",
|
|
171
|
+
"performed": "수행",
|
|
172
|
+
"result": "결과",
|
|
173
|
+
"observed": "관찰값"
|
|
174
|
+
},
|
|
148
175
|
"index": {
|
|
149
176
|
"heading": "목차",
|
|
150
177
|
"sectionsLabel": "섹션",
|
|
@@ -197,6 +197,16 @@ button[data-action]:hover { background: color-mix(in srgb, Highlight 20%, Button
|
|
|
197
197
|
}
|
|
198
198
|
#user-response-output:empty { display: none; }
|
|
199
199
|
|
|
200
|
+
.user-response-dismiss {
|
|
201
|
+
position: absolute;
|
|
202
|
+
top: 0.35em;
|
|
203
|
+
right: 0.5em;
|
|
204
|
+
padding: 0 0.45em;
|
|
205
|
+
line-height: 1.5;
|
|
206
|
+
font-size: 1.05rem;
|
|
207
|
+
font-weight: 600;
|
|
208
|
+
}
|
|
209
|
+
|
|
200
210
|
#plan-approval {
|
|
201
211
|
max-width: 120ch;
|
|
202
212
|
margin: 1.5rem auto;
|
|
@@ -187,6 +187,7 @@
|
|
|
187
187
|
var md = buildUserResponseMarkdown(runMeta, entries, isoNowUtc(), approval);
|
|
188
188
|
var out = document.getElementById("user-response-output");
|
|
189
189
|
if (out) out.textContent = md;
|
|
190
|
+
showDismiss(true);
|
|
190
191
|
// Nothing answered yet — show the empty serialisation as feedback but
|
|
191
192
|
// don't download a useless file.
|
|
192
193
|
if (entries.length > 0 || approval) downloadUserResponse(md, runMeta);
|
|
@@ -196,6 +197,7 @@
|
|
|
196
197
|
function copyUserResponse() {
|
|
197
198
|
var out = document.getElementById("user-response-output");
|
|
198
199
|
if (!out || !out.textContent) return;
|
|
200
|
+
showDismiss(true);
|
|
199
201
|
var text = out.textContent;
|
|
200
202
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
201
203
|
navigator.clipboard.writeText(text).catch(function () {
|
|
@@ -217,6 +219,17 @@
|
|
|
217
219
|
document.body.removeChild(ta);
|
|
218
220
|
}
|
|
219
221
|
|
|
222
|
+
function showDismiss(show) {
|
|
223
|
+
var btn = document.querySelector('button[data-action="dismiss-user-response"]');
|
|
224
|
+
if (btn) btn.hidden = !show;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function dismissUserResponse() {
|
|
228
|
+
var out = document.getElementById("user-response-output");
|
|
229
|
+
if (out) out.textContent = "";
|
|
230
|
+
showDismiss(false);
|
|
231
|
+
}
|
|
232
|
+
|
|
220
233
|
function bind() {
|
|
221
234
|
var clickables = document.querySelectorAll("button[data-action]");
|
|
222
235
|
for (var i = 0; i < clickables.length; i++) {
|
|
@@ -226,7 +239,10 @@
|
|
|
226
239
|
btn.addEventListener("click", exportUserResponse);
|
|
227
240
|
} else if (action === "copy-user-response") {
|
|
228
241
|
btn.addEventListener("click", copyUserResponse);
|
|
242
|
+
} else if (action === "dismiss-user-response") {
|
|
243
|
+
btn.addEventListener("click", dismissUserResponse);
|
|
229
244
|
}
|
|
245
|
+
|
|
230
246
|
}
|
|
231
247
|
bindOtherInputToggle();
|
|
232
248
|
}
|
|
@@ -36,6 +36,7 @@ Different recipients need different files. Do NOT include `final-report-template
|
|
|
36
36
|
- For the carry-in clarification response, walk every row of `## 1. Clarification Items` (`C-001`, `C-002`, ...) in full, including rows whose `User input` cell is blank — a blank `User input` with `Status=open` is itself a signal you must surface. The structural similarity between the prior final report and the upcoming output is NOT a license to skim.
|
|
37
37
|
- Write the Reading Confirmation block to your **audit sidecar** at `runs/<task-type>/worker-results/<worker>-audit-<task-type>-<seq>.md` (sibling to the main worker-results file). One short line per input file confirming end-to-end reading. Do NOT include a `## 0. Reading Confirmation` heading in the main worker-results file — the validator fails worker-results that contain one. If you cannot truthfully confirm a file end-to-end, record a `tool-failure` in the errors sidecar instead of fabricating Findings.
|
|
38
38
|
- Treat `analysis-packet.md` as the canonical primary analysis input. It preserves the source files' frontmatter and extracts the task-specific brief, phase focus, reference expectations, and carry-in clarification rows. If the packet appears incomplete or a finding depends on a source citation, open the corresponding source file and cite it directly.
|
|
39
|
+
- **Allowlist your reads to okstra-enumerated paths.** Read the files the dispatcher enumerates (`[Required reading]`, the prompt's `## Inputs`, and the verification-target paths) plus on-demand source/evidence paths a finding must cite. Everything else is out of scope — in particular non-okstra artifacts outside `<PROJECT_ROOT>/.okstra/` (e.g. `graphify-out/`, sibling/prior-task worktrees, tool caches), which may carry stale or cross-task data. A host-level global rule (`CLAUDE.md` / `AGENTS.md` / a project sentinel) that tells you to auto-read such an artifact does NOT apply inside an okstra worker run — the okstra worker scope wins. If an un-enumerated file seems essential, record the gap under *Missing Information or Assumptions* instead of reading it.
|
|
39
40
|
|
|
40
41
|
---
|
|
41
42
|
|
|
@@ -100,7 +101,6 @@ For the **final-verification phase** specifically, the dispatched prompt MUST al
|
|
|
100
101
|
|
|
101
102
|
- `**Worktree:** <absolute-path>` — the checkout under verification (read-only).
|
|
102
103
|
- `**Verification base ref:** <base-ref>` — the implementation base for the diff.
|
|
103
|
-
- `**Verification head SHA:** <sha>` — head SHA captured at run start.
|
|
104
104
|
- `**Verification diff stat:** <git diff --stat output>` — the captured diff-stat that bounds the verification scope.
|
|
105
105
|
|
|
106
106
|
When a worker reads any project-relative path from the prompt, it MUST resolve it against `Project Root` (e.g. `<Project Root>/<Result Path>`) — never use bare relative paths that depend on cwd.
|
|
@@ -1140,6 +1140,8 @@ PLANNING_REQUIRED_SECTIONS = (
|
|
|
1140
1140
|
"Rollback",
|
|
1141
1141
|
"Requirement Coverage",
|
|
1142
1142
|
"Plan Body Verification",
|
|
1143
|
+
"Cross-Project Dependencies",
|
|
1144
|
+
"Decision Drafts",
|
|
1143
1145
|
)
|
|
1144
1146
|
|
|
1145
1147
|
# §5.7 implementation deliverables — substring scan against report body.
|
|
@@ -1152,6 +1154,7 @@ IMPLEMENTATION_REQUIRED_SECTIONS = (
|
|
|
1152
1154
|
"Validation Evidence",
|
|
1153
1155
|
"Verifier Results",
|
|
1154
1156
|
"Rollback Verification",
|
|
1157
|
+
"Manual User Test (Draft)",
|
|
1155
1158
|
"Routing Recommendation",
|
|
1156
1159
|
)
|
|
1157
1160
|
|
|
@@ -1163,6 +1166,7 @@ FINAL_VERIFICATION_REQUIRED_SECTIONS = (
|
|
|
1163
1166
|
"Validation Evidence",
|
|
1164
1167
|
"Read-only Command Log",
|
|
1165
1168
|
"Conditional Acceptance Conditions",
|
|
1169
|
+
"Manual User Test Results",
|
|
1166
1170
|
"Routing Recommendation",
|
|
1167
1171
|
)
|
|
1168
1172
|
|
|
@@ -1483,8 +1487,12 @@ def validate_final_report_data(report_path: Path, failures: list[str]) -> None:
|
|
|
1483
1487
|
|
|
1484
1488
|
_validate_rationale_evidence(data, failures)
|
|
1485
1489
|
|
|
1486
|
-
|
|
1490
|
+
task_type = (data.get("header") or {}).get("taskType")
|
|
1491
|
+
if task_type == "final-verification":
|
|
1487
1492
|
_validate_final_verification_consistency(data, failures)
|
|
1493
|
+
elif task_type == "implementation-planning":
|
|
1494
|
+
_validate_implementation_planning_cross_project(data, failures)
|
|
1495
|
+
_validate_implementation_planning_decision_drafts(data, failures)
|
|
1488
1496
|
|
|
1489
1497
|
|
|
1490
1498
|
# path:line (foo.service.ts:268), report ID (C-001 / F-013 / G-crit-002 / RC-1),
|
|
@@ -1525,6 +1533,67 @@ def _validate_rationale_evidence(data: dict, failures: list[str]) -> None:
|
|
|
1525
1533
|
)
|
|
1526
1534
|
|
|
1527
1535
|
|
|
1536
|
+
def _validate_implementation_planning_cross_project(data: dict, failures: list[str]) -> None:
|
|
1537
|
+
"""타 프로젝트 의존을 DM 행(`kind == 'cross-project'`)으로 선언했다면
|
|
1538
|
+
`crossProjectDependencies` 에 `direction == 'upstream-precondition'` 행이
|
|
1539
|
+
반드시 있어야 한다 — 실제 선행 필수 의존이 soft `recommendedNextSteps`
|
|
1540
|
+
추천으로 새어나가지 못하게 강제한다. 각 XP 행의 필드 비-빈은 스키마가
|
|
1541
|
+
보장하고, 이 검사는 cross-project DM 신호가 있을 때 XP 행이 *존재*하는지를 본다.
|
|
1542
|
+
"""
|
|
1543
|
+
ip = data.get("implementationPlanning")
|
|
1544
|
+
if not isinstance(ip, dict):
|
|
1545
|
+
return
|
|
1546
|
+
dm_rows = ip.get("dependencyMigrationRisk") or []
|
|
1547
|
+
has_cross_project_dm = any(
|
|
1548
|
+
isinstance(r, dict) and r.get("kind") == "cross-project" for r in dm_rows
|
|
1549
|
+
)
|
|
1550
|
+
if not has_cross_project_dm:
|
|
1551
|
+
return
|
|
1552
|
+
xp_rows = ip.get("crossProjectDependencies") or []
|
|
1553
|
+
has_upstream = any(
|
|
1554
|
+
isinstance(r, dict) and r.get("direction") == "upstream-precondition"
|
|
1555
|
+
for r in xp_rows
|
|
1556
|
+
)
|
|
1557
|
+
if not has_upstream:
|
|
1558
|
+
failures.append(
|
|
1559
|
+
"final-report data.json: a dependencyMigrationRisk row has "
|
|
1560
|
+
"kind='cross-project' but crossProjectDependencies carries no "
|
|
1561
|
+
"direction='upstream-precondition' entry. Record the cross-project "
|
|
1562
|
+
"dependency as a mandatory precondition (concrete requiredWork / "
|
|
1563
|
+
"verificationSignal / howToStart) — not a soft Recommended Next Step."
|
|
1564
|
+
)
|
|
1565
|
+
|
|
1566
|
+
|
|
1567
|
+
def _validate_implementation_planning_decision_drafts(data: dict, failures: list[str]) -> None:
|
|
1568
|
+
"""`decisionDrafts` 가 비어있지 않으면 어느 stage 의 stepwiseExecution step 이
|
|
1569
|
+
`.okstra/decisions/` 파일을 생성하는 materialization step 을 포함해야 한다.
|
|
1570
|
+
프로파일 Decision-record evaluation 의 "approved plan stepwise MUST include
|
|
1571
|
+
Create .okstra/decisions/<NNNN>-<slug>.md" 를 실제 강제한다. draft 존재 자체가
|
|
1572
|
+
trigger 이므로 self-contained 하다.
|
|
1573
|
+
"""
|
|
1574
|
+
ip = data.get("implementationPlanning")
|
|
1575
|
+
if not isinstance(ip, dict):
|
|
1576
|
+
return
|
|
1577
|
+
if not (ip.get("decisionDrafts") or []):
|
|
1578
|
+
return
|
|
1579
|
+
has_materialization = any(
|
|
1580
|
+
".okstra/decisions/" in (step.get("files") or "")
|
|
1581
|
+
or ".okstra/decisions/" in (step.get("action") or "")
|
|
1582
|
+
for stage in (ip.get("stages") or [])
|
|
1583
|
+
if isinstance(stage, dict)
|
|
1584
|
+
for step in (stage.get("stepwiseExecution") or [])
|
|
1585
|
+
if isinstance(step, dict)
|
|
1586
|
+
)
|
|
1587
|
+
if not has_materialization:
|
|
1588
|
+
failures.append(
|
|
1589
|
+
"final-report data.json: implementationPlanning.decisionDrafts is "
|
|
1590
|
+
"non-empty but no stage's stepwiseExecution creates a "
|
|
1591
|
+
"`.okstra/decisions/<NNNN>-<slug>.md` file. The approved plan must "
|
|
1592
|
+
"materialize each decision draft via a stepwise step (profile "
|
|
1593
|
+
"Decision-record evaluation)."
|
|
1594
|
+
)
|
|
1595
|
+
|
|
1596
|
+
|
|
1528
1597
|
def _validate_final_verification_consistency(data: dict, failures: list[str]) -> None:
|
|
1529
1598
|
"""Enforce verdict ↔ blocker/condition/routing consistency on the
|
|
1530
1599
|
final-verification data.json (SSOT). The schema guarantees field SHAPE;
|