okstra 0.147.0 → 0.148.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/README.md +21 -7
- package/docs/architecture/storage-model.md +34 -61
- package/docs/architecture.md +51 -49
- package/docs/cli.md +38 -21
- package/docs/for-ai/skills/okstra-run.md +13 -34
- package/docs/performance-improvement-plan-v2.md +2 -2
- package/docs/pr-template-usage.md +1 -1
- package/docs/project-structure-overview.md +10 -8
- package/docs/task-process/README.md +4 -4
- package/docs/task-process/common-flow.md +12 -12
- package/docs/task-process/final-verification.md +2 -2
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +2 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/codex-worker.md +2 -2
- package/runtime/agents/workers/grok-worker.md +256 -0
- package/runtime/agents/workers/kimi-worker.md +256 -0
- package/runtime/agents/workers/report-writer-worker.md +2 -2
- package/runtime/bin/lib/okstra/cli.sh +13 -1
- package/runtime/bin/lib/okstra/globals.sh +3 -0
- package/runtime/bin/lib/okstra/usage.sh +17 -12
- package/runtime/bin/okstra-grok-exec.sh +5 -0
- package/runtime/bin/okstra-kimi-exec.sh +5 -0
- package/runtime/bin/okstra-provider-exec.py +235 -0
- package/runtime/bin/okstra.sh +3 -0
- package/runtime/prompts/lead/adapters/antigravity.md +48 -0
- package/runtime/prompts/lead/adapters/claude-code.md +13 -11
- package/runtime/prompts/lead/adapters/codex.md +7 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/prompts/lead/report-writer.md +1 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +4 -4
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-executor.md +12 -12
- package/runtime/prompts/profiles/_implementation-self-check.md +4 -4
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/change-impact-analysis.md +2 -0
- package/runtime/prompts/profiles/error-analysis.md +2 -0
- package/runtime/prompts/profiles/feature-analysis.md +2 -0
- package/runtime/prompts/profiles/final-verification.md +3 -1
- package/runtime/prompts/profiles/forbidden-actions.json +4 -4
- package/runtime/prompts/profiles/implementation-planning.md +3 -1
- package/runtime/prompts/profiles/implementation.md +2 -2
- package/runtime/prompts/profiles/improvement-discovery.md +3 -1
- package/runtime/prompts/profiles/project-analysis.md +2 -0
- package/runtime/prompts/profiles/release-handoff.md +7 -7
- package/runtime/prompts/profiles/requirements-discovery.md +2 -0
- package/runtime/prompts/wizard/prompts.ko.json +9 -1
- package/runtime/python/okstra_ctl/codex_dispatch.py +68 -87
- package/runtime/python/okstra_ctl/dispatch_core.py +4 -22
- package/runtime/python/okstra_ctl/lead_events.py +1 -1
- package/runtime/python/okstra_ctl/lead_runtime.py +13 -2
- package/runtime/python/okstra_ctl/models.py +156 -8
- package/runtime/python/okstra_ctl/path_hints.py +9 -25
- package/runtime/python/okstra_ctl/paths.py +1 -1
- package/runtime/python/okstra_ctl/render.py +172 -74
- package/runtime/python/okstra_ctl/report_html/common.py +38 -2
- package/runtime/python/okstra_ctl/report_html/filters.py +104 -0
- package/runtime/python/okstra_ctl/report_html/render.py +7 -0
- package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +2 -1
- package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +2 -1
- package/runtime/python/okstra_ctl/report_html/visualizations.py +32 -6
- package/runtime/python/okstra_ctl/run.py +264 -45
- package/runtime/python/okstra_ctl/runner_resolution.py +103 -0
- package/runtime/python/okstra_ctl/team.py +2 -7
- package/runtime/python/okstra_ctl/wizard.py +194 -21
- package/runtime/python/okstra_ctl/worker_artifacts.py +46 -0
- package/runtime/python/okstra_ctl/workers.py +3 -1
- package/runtime/python/okstra_ctl/workflow.py +4 -2
- package/runtime/python/okstra_token_usage/__init__.py +1 -0
- package/runtime/python/okstra_token_usage/collect.py +32 -23
- package/runtime/python/okstra_token_usage/pricing.py +35 -3
- package/runtime/schemas/final-report-v2.0.schema.json +2 -2
- package/runtime/skills/okstra-run/SKILL.md +31 -42
- package/runtime/templates/prd/pr-body.template.md +1 -1
- package/runtime/templates/reports/html/assets/base.css +6 -3
- package/runtime/templates/reports/html/base.template.html +22 -8
- package/runtime/templates/reports/html/macros/forms.html +2 -2
- package/runtime/templates/reports/html/macros/layout.html +5 -5
- package/runtime/templates/reports/html/macros/visualizations.html +11 -1
- package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +11 -11
- package/runtime/templates/reports/html/tasks/error-analysis.template.html +12 -12
- package/runtime/templates/reports/html/tasks/feature-analysis.template.html +9 -9
- package/runtime/templates/reports/html/tasks/final-verification.template.html +7 -7
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +12 -12
- package/runtime/templates/reports/html/tasks/implementation.template.html +7 -7
- package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +6 -6
- package/runtime/templates/reports/html/tasks/project-analysis.template.html +11 -11
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +5 -5
- package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +8 -8
- package/runtime/templates/reports/report.js +21 -4
- package/runtime/templates/reports/settings.template.json +4 -0
- package/runtime/templates/reports/task-brief.template.md +7 -7
- package/runtime/validators/validate-run.py +11 -6
- package/runtime/validators/validate_session_conformance.py +2 -1
- package/src/cli-registry.mjs +4 -4
- package/src/commands/execute/codex-dispatch.mjs +7 -10
- package/src/commands/execute/render-bundle.mjs +3 -3
- package/src/commands/execute/run.mjs +17 -52
- package/src/commands/execute/wizard.mjs +4 -1
- package/src/commands/lifecycle/doctor.mjs +6 -3
- package/src/commands/lifecycle/install.mjs +31 -8
- package/src/lib/runtime-manifest.mjs +1 -1
- package/src/lib/runtime-resolver.mjs +2 -2
- package/src/lib/worker-agent-render.mjs +50 -0
|
@@ -6,34 +6,34 @@
|
|
|
6
6
|
<section data-report-section="verification-verdict">
|
|
7
7
|
<h2>검증 판정</h2>
|
|
8
8
|
<p><span class="status status-{{ verdict.verdictToken }}">{{ verdict.verdictToken }}</span></p>
|
|
9
|
-
{{ render_narrative(narrative.verdictExplanation, "finalVerification.userNarrative.verdictExplanation") }}
|
|
9
|
+
{{ render_narrative(narrative.verdictExplanation, "finalVerification.userNarrative.verdictExplanation", evidenceIndex) }}
|
|
10
10
|
</section>
|
|
11
11
|
|
|
12
12
|
<section data-report-section="requirement-results" data-report-field="finalVerification.validationEvidence">
|
|
13
13
|
<h2>요구사항별 결과</h2>
|
|
14
|
-
{{ render_narrative(narrative.coverageExplanation, "finalVerification.userNarrative.coverageExplanation") }}
|
|
14
|
+
{{ render_narrative(narrative.coverageExplanation, "finalVerification.userNarrative.coverageExplanation", evidenceIndex) }}
|
|
15
15
|
{{ figure(coverageFigure) }}
|
|
16
16
|
</section>
|
|
17
17
|
|
|
18
18
|
<section data-report-section="blockers" data-report-field="finalVerification.acceptanceBlockers">
|
|
19
19
|
<h2>수락 차단 항목</h2>
|
|
20
|
-
{{ render_narrative(narrative.blockerExplanation, "finalVerification.userNarrative.blockerExplanation") }}
|
|
20
|
+
{{ render_narrative(narrative.blockerExplanation, "finalVerification.userNarrative.blockerExplanation", evidenceIndex) }}
|
|
21
21
|
<div class="summary-grid">{% for row in final.acceptanceBlockers %}{{ summary_card(row.id ~ " · " ~ row.severity, row.statement, "important") }}{% else %}<p>수락을 막는 항목이 없습니다.</p>{% endfor %}</div>
|
|
22
22
|
</section>
|
|
23
23
|
|
|
24
24
|
<section data-report-section="residual-risk" data-report-field="finalVerification.residualRisk">
|
|
25
25
|
<h2>잔여 위험</h2>
|
|
26
|
-
<div class="summary-grid">{% for row in final.residualRisk %}<article class="summary-card"><h3>{{ row.id }}</h3><p>{{ row.item }}</p><p><strong>담당:</strong> {{ row.owner }}</p><p><strong>상향 조건:</strong> {{ row.escalationTrigger }}</p></article>{% else %}<p>기록된 잔여 위험이 없습니다.</p>{% endfor %}</div>
|
|
26
|
+
<div class="summary-grid">{% for row in final.residualRisk %}<article class="summary-card"><h3>{{ row.id }}</h3><p>{{ row.item | inline_code }}</p><p><strong>담당:</strong> {{ row.owner | inline_code }}</p><p><strong>상향 조건:</strong> {{ row.escalationTrigger | inline_code }}</p></article>{% else %}<p>기록된 잔여 위험이 없습니다.</p>{% endfor %}</div>
|
|
27
27
|
</section>
|
|
28
28
|
|
|
29
29
|
<section data-report-section="manual-results" data-report-field="finalVerification.manualUserTest">
|
|
30
30
|
<h2>수동 사용자 테스트 결과</h2>
|
|
31
|
-
{% if final.manualUserTest.applicable %}{% for row in final.manualUserTest.results %}<article class="summary-card tone-{{ row.result }}"><h3>{{ row.target }} · {{ row.result }}</h3><p>{{ row.performed }}</p><p>{{ row.observed }}</p></article>{% endfor %}{% else %}<p>{{ final.manualUserTest.exemptionReaffirm }}</p>{% endif %}
|
|
31
|
+
{% if final.manualUserTest.applicable %}{% for row in final.manualUserTest.results %}<article class="summary-card tone-{{ row.result }}"><h3>{{ row.target | inline_code }} · {{ row.result }}</h3><p>{{ row.performed | inline_code }}</p><p>{{ row.observed | inline_code }}</p></article>{% endfor %}{% else %}<p>{{ final.manualUserTest.exemptionReaffirm | inline_code }}</p>{% endif %}
|
|
32
32
|
</section>
|
|
33
33
|
|
|
34
34
|
<section data-report-section="release-route">
|
|
35
35
|
<h2>릴리스 경로</h2>
|
|
36
|
-
{{ render_narrative(narrative.releaseReadiness, "finalVerification.userNarrative.releaseReadiness") }}
|
|
37
|
-
{% if releaseAllowed %}<p>{{ final.routingRecommendation }}</p>{% else %}<p>현재 판정으로는 release-handoff를 시작할 수 없습니다. 차단 또는 조건을 먼저 해소하세요.</p>{% endif %}
|
|
36
|
+
{{ render_narrative(narrative.releaseReadiness, "finalVerification.userNarrative.releaseReadiness", evidenceIndex) }}
|
|
37
|
+
{% if releaseAllowed %}<p>{{ final.routingRecommendation | inline_code }}</p>{% else %}<p>현재 판정으로는 release-handoff를 시작할 수 없습니다. 차단 또는 조건을 먼저 해소하세요.</p>{% endif %}
|
|
38
38
|
</section>
|
|
39
39
|
{% endblock %}
|
|
@@ -6,35 +6,35 @@
|
|
|
6
6
|
{% block human_content %}
|
|
7
7
|
<section data-report-section="planning-goal">
|
|
8
8
|
<h2>계획 목표</h2>
|
|
9
|
-
<p>{{ humanSummary.outcome }}</p>
|
|
10
|
-
<div class="summary-grid">{% for reason in humanSummary.whyItMatters %}{{ summary_card("왜 중요한가", reason, "important") }}{% endfor %}</div>
|
|
9
|
+
<p>{{ humanSummary.outcome | inline_code }}</p>
|
|
10
|
+
<div class="summary-grid">{% for reason in humanSummary.whyItMatters %}{{ summary_card("왜 중요한가 " ~ loop.index, reason, "important") }}{% endfor %}</div>
|
|
11
11
|
</section>
|
|
12
12
|
|
|
13
13
|
<section data-report-section="option-comparison">
|
|
14
14
|
<h2>구현 대안 비교</h2>
|
|
15
|
-
{{ render_narrative(narrative.optionExplanation, "implementationPlanning.userNarrative.optionExplanation") }}
|
|
16
|
-
<div class="summary-grid" data-report-field="implementationPlanning.optionCandidates">{% for row in planning.optionCandidates %}<article class="summary-card"><h3>{{ row.name }}</h3><p><strong>인터페이스:</strong> {{ row.interfaces }}</p><p><strong>영향 범위:</strong> {{ row.blastRadius }}</p><ul>{% for file in row.fileStructure %}<li>{{ file.action }} <code>{{ file.path }}</code> — {{ file.summary }}</li>{% endfor %}</ul></article>{% endfor %}</div>
|
|
17
|
-
<table data-report-field="implementationPlanning.tradeoffMatrix"><thead><tr><th>옵션</th><th>복잡도</th><th>위험</th><th>가역성</th><th>테스트 비용</th><th>배포 비용</th></tr></thead><tbody>{% for row in planning.tradeoffMatrix %}<tr><td>{{ row.option }}</td><td>{{ row.complexity }}</td><td>{{ row.risk }}</td><td>{{ row.reversibility }}</td><td>{{ row.testCoverageCost }}</td><td>{{ row.rolloutCost }}</td></tr>{% endfor %}</tbody></table>
|
|
15
|
+
{{ render_narrative(narrative.optionExplanation, "implementationPlanning.userNarrative.optionExplanation", evidenceIndex) }}
|
|
16
|
+
<div class="summary-grid" data-report-field="implementationPlanning.optionCandidates">{% for row in planning.optionCandidates %}<article class="summary-card"><h3>{{ row.name | inline_code }}</h3><p><strong>인터페이스:</strong> {{ row.interfaces | inline_code }}</p><p><strong>영향 범위:</strong> {{ row.blastRadius | inline_code }}</p><ul>{% for file in row.fileStructure %}<li>{{ file.action }} <code>{{ file.path }}</code> — {{ file.summary | inline_code }}</li>{% endfor %}</ul></article>{% endfor %}</div>
|
|
17
|
+
<table data-report-field="implementationPlanning.tradeoffMatrix"><thead><tr><th>옵션</th><th>복잡도</th><th>위험</th><th>가역성</th><th>테스트 비용</th><th>배포 비용</th></tr></thead><tbody>{% for row in planning.tradeoffMatrix %}<tr><td>{{ row.option | inline_code }}</td><td>{{ row.complexity | inline_code }}</td><td>{{ row.risk | inline_code }}</td><td>{{ row.reversibility | inline_code }}</td><td>{{ row.testCoverageCost | inline_code }}</td><td>{{ row.rolloutCost | inline_code }}</td></tr>{% endfor %}</tbody></table>
|
|
18
18
|
</section>
|
|
19
19
|
|
|
20
20
|
<section data-report-section="recommendation" data-report-field="implementationPlanning.recommendedOption">
|
|
21
21
|
<h2>권장안</h2>
|
|
22
|
-
{{ render_narrative(narrative.recommendationExplanation, "implementationPlanning.userNarrative.recommendationExplanation") }}
|
|
23
|
-
<article class="summary-card tone-important"><h3>{{ planning.recommendedOption.name }}</h3><p>{{ planning.recommendedOption.coreReason }}</p><p>{{ planning.recommendedOption.rationale }}</p><p><strong>선택하지 않은 안:</strong> {{ planning.recommendedOption.rejectedSummary }}</p></article>
|
|
22
|
+
{{ render_narrative(narrative.recommendationExplanation, "implementationPlanning.userNarrative.recommendationExplanation", evidenceIndex) }}
|
|
23
|
+
<article class="summary-card tone-important"><h3>{{ planning.recommendedOption.name | inline_code }}</h3><p>{{ planning.recommendedOption.coreReason | inline_code }}</p><p>{{ planning.recommendedOption.rationale | inline_code }}</p><p><strong>선택하지 않은 안:</strong> {{ planning.recommendedOption.rejectedSummary | inline_code }}</p></article>
|
|
24
24
|
</section>
|
|
25
25
|
|
|
26
26
|
<section data-report-section="stage-map" data-report-field="implementationPlanning.stageMap">
|
|
27
27
|
<h2>단계별 실행 지도</h2>
|
|
28
|
-
{{ render_narrative(narrative.stageStrategy, "implementationPlanning.userNarrative.stageStrategy") }}
|
|
28
|
+
{{ render_narrative(narrative.stageStrategy, "implementationPlanning.userNarrative.stageStrategy", evidenceIndex) }}
|
|
29
29
|
{{ figure(stageFigure) }}
|
|
30
|
-
<div class="summary-grid">{% for row in planning.stages %}<article class="summary-card"><h3>Stage {{ row.stage }} · {{ row.title }}</h3><p>{{ row.sliceValue }}</p><p><strong>완료 조건:</strong> {{ row.acceptance }}</p><p><strong>검증:</strong> {{ row.stageValidation }}</p></article>{% endfor %}</div>
|
|
30
|
+
<div class="summary-grid">{% for row in planning.stages %}<article class="summary-card"><h3>Stage {{ row.stage }} · {{ row.title | inline_code }}</h3><p>{{ row.sliceValue | inline_code }}</p><p><strong>완료 조건:</strong> {{ row.acceptance | inline_code }}</p><p><strong>검증:</strong> {{ row.stageValidation | inline_code }}</p></article>{% endfor %}</div>
|
|
31
31
|
</section>
|
|
32
32
|
|
|
33
33
|
<section data-report-section="validation-and-rollback">
|
|
34
34
|
<h2>검증과 롤백</h2>
|
|
35
|
-
{{ render_narrative(narrative.validationAndRollback, "implementationPlanning.userNarrative.validationAndRollback") }}
|
|
36
|
-
<h3>검증 체크리스트</h3><table data-report-field="implementationPlanning.validationChecklist"><thead><tr><th>ID</th><th>시점</th><th>확인</th><th>명령</th><th>기대 결과</th></tr></thead><tbody>{% for row in planning.validationChecklist %}<tr><td>{{ row.id }}</td><td>{{ row.phase }}</td><td>{{ row.check }}</td><td><code>{{ row.commandOrObservation }}</code></td><td>{{ row.expectedOutcome }}</td></tr>{% endfor %}</tbody></table>
|
|
37
|
-
<h3>롤백 전략</h3><div class="summary-grid" data-report-field="implementationPlanning.rollbackStrategy">{% for row in planning.rollbackStrategy %}{{ summary_card(row.id ~ " · " ~ row.triggerSignal, row.action, "important") }}{% endfor %}</div>
|
|
35
|
+
{{ render_narrative(narrative.validationAndRollback, "implementationPlanning.userNarrative.validationAndRollback", evidenceIndex) }}
|
|
36
|
+
<h3>검증 체크리스트</h3><table data-report-field="implementationPlanning.validationChecklist"><thead><tr><th>ID</th><th>시점</th><th>확인</th><th>명령</th><th>기대 결과</th></tr></thead><tbody>{% for row in planning.validationChecklist %}<tr><td>{{ row.id }}</td><td>{{ row.phase }}</td><td>{{ row.check | inline_code }}</td><td><code>{{ row.commandOrObservation }}</code></td><td>{{ row.expectedOutcome | inline_code }}</td></tr>{% endfor %}</tbody></table>
|
|
37
|
+
<h3>롤백 전략</h3><div class="summary-grid" data-report-field="implementationPlanning.rollbackStrategy">{% for row in planning.rollbackStrategy %}{{ summary_card(row.id ~ " · " ~ (row.triggerSignal | inline_code), row.action, "important") }}{% endfor %}</div>
|
|
38
38
|
</section>
|
|
39
39
|
|
|
40
40
|
<section data-report-section="open-decisions">
|
|
@@ -5,36 +5,36 @@
|
|
|
5
5
|
{% block human_content %}
|
|
6
6
|
<section data-report-section="delivered-outcome">
|
|
7
7
|
<h2>전달된 결과</h2>
|
|
8
|
-
{{ render_narrative(narrative.deliveredOutcome, "implementation.userNarrative.deliveredOutcome") }}
|
|
9
|
-
<p>{{ humanSummary.outcome }}</p>
|
|
8
|
+
{{ render_narrative(narrative.deliveredOutcome, "implementation.userNarrative.deliveredOutcome", evidenceIndex) }}
|
|
9
|
+
<p>{{ humanSummary.outcome | inline_code }}</p>
|
|
10
10
|
</section>
|
|
11
11
|
|
|
12
12
|
<section data-report-section="change-map" data-report-field="implementation.diffSummary">
|
|
13
13
|
<h2>무엇이 달라졌는가</h2>
|
|
14
|
-
{{ render_narrative(narrative.changeExplanation, "implementation.userNarrative.changeExplanation") }}
|
|
14
|
+
{{ render_narrative(narrative.changeExplanation, "implementation.userNarrative.changeExplanation", evidenceIndex) }}
|
|
15
15
|
{{ figure(changeFigure) }}
|
|
16
16
|
</section>
|
|
17
17
|
|
|
18
18
|
<section data-report-section="requirement-coverage">
|
|
19
19
|
<h2>요구사항 충족</h2>
|
|
20
|
-
<table data-report-field="implementation.requirementCoverage"><thead><tr><th>ID</th><th>요구사항</th><th>구현·증거</th><th>상태</th></tr></thead><tbody>{% for row in implementation.requirementCoverage %}<tr><td>{{ row.id }}</td><td>{{ row.requirement }}</td><td>{{ row.coveredBy }}</td><td><span class="status status-{{ row.status }}">{{ row.status }}</span></td></tr>{% endfor %}</tbody></table>
|
|
20
|
+
<table data-report-field="implementation.requirementCoverage"><thead><tr><th>ID</th><th>요구사항</th><th>구현·증거</th><th>상태</th></tr></thead><tbody>{% for row in implementation.requirementCoverage %}<tr><td>{{ row.id }}</td><td>{{ row.requirement | inline_code }}</td><td>{{ row.coveredBy | inline_code }}</td><td><span class="status status-{{ row.status }}">{{ row.status }}</span></td></tr>{% endfor %}</tbody></table>
|
|
21
21
|
</section>
|
|
22
22
|
|
|
23
23
|
<section data-report-section="validation" data-report-field="implementation.validationEvidence">
|
|
24
24
|
<h2>검증 결과</h2>
|
|
25
|
-
{{ render_narrative(narrative.validationExplanation, "implementation.userNarrative.validationExplanation") }}
|
|
25
|
+
{{ render_narrative(narrative.validationExplanation, "implementation.userNarrative.validationExplanation", evidenceIndex) }}
|
|
26
26
|
<div class="summary-grid">{% for row in implementation.validationEvidence %}{{ summary_card(row.phase ~ " · 종료 코드 " ~ row.exitCode, row.outputTail, "important" if row.exitCode else "neutral") }}{% endfor %}</div>
|
|
27
27
|
<p><strong>독립 검증:</strong> {% for row in implementation.verifierResults %}<span class="status status-{{ row.verdict | lower }}">{{ row.verdict }}</span>{% if not loop.last %}, {% endif %}{% endfor %}</p>
|
|
28
28
|
</section>
|
|
29
29
|
|
|
30
30
|
<section data-report-section="remaining-issues">
|
|
31
31
|
<h2>남은 문제</h2>
|
|
32
|
-
{{ render_narrative(narrative.remainingIssues, "implementation.userNarrative.remainingIssues") }}
|
|
32
|
+
{{ render_narrative(narrative.remainingIssues, "implementation.userNarrative.remainingIssues", evidenceIndex) }}
|
|
33
33
|
<div class="summary-grid">{% for row in implementation.outOfPlanEdits %}{{ summary_card(row.id ~ " · " ~ row.file, row.rationale, "important") }}{% else %}<p>계획 밖 수정 사항이 없습니다.</p>{% endfor %}</div>
|
|
34
34
|
</section>
|
|
35
35
|
|
|
36
36
|
<section data-report-section="manual-test" data-report-field="implementation.manualUserTest">
|
|
37
37
|
<h2>수동 사용자 테스트</h2>
|
|
38
|
-
{% if implementation.manualUserTest.applicable %}{% for row in implementation.manualUserTest["items"] %}<article class="summary-card"><h3>{{ row.target }}</h3><p><strong>환경:</strong> {{ row.environmentSetup }}</p><ol>{% for step in row.steps %}<li>{{ step }}</li>{% endfor %}</ol><p><strong>기대 결과:</strong> {{ row.expectedResult }}</p></article>{% endfor %}{% else %}<p>{{ implementation.manualUserTest.exemptionReason }}</p>{% endif %}
|
|
38
|
+
{% if implementation.manualUserTest.applicable %}{% for row in implementation.manualUserTest["items"] %}<article class="summary-card"><h3>{{ row.target | inline_code }}</h3><p><strong>환경:</strong> {{ row.environmentSetup | inline_code }}</p><ol>{% for step in row.steps %}<li>{{ step | inline_code }}</li>{% endfor %}</ol><p><strong>기대 결과:</strong> {{ row.expectedResult | inline_code }}</p></article>{% endfor %}{% else %}<p>{{ implementation.manualUserTest.exemptionReason | inline_code }}</p>{% endif %}
|
|
39
39
|
</section>
|
|
40
40
|
{% endblock %}
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
{% block human_content %}
|
|
6
6
|
<section data-report-section="opportunities">
|
|
7
7
|
<h2>발견한 개선 기회</h2>
|
|
8
|
-
{{ render_narrative(narrative.opportunityOverview, "improvementDiscovery.userNarrative.opportunityOverview") }}
|
|
9
|
-
<table data-report-field="improvementDiscovery.lensCoverage"><thead><tr><th>렌즈</th><th>결과</th><th>설명</th><th>근거</th></tr></thead><tbody>{% for row in improvement.lensCoverage %}<tr><td>{{ row.lens }}</td><td>{{ row.status }}</td><td>{{ row.rationale }}</td><td>{{ row.evidence |
|
|
8
|
+
{{ render_narrative(narrative.opportunityOverview, "improvementDiscovery.userNarrative.opportunityOverview", evidenceIndex) }}
|
|
9
|
+
<table data-report-field="improvementDiscovery.lensCoverage"><thead><tr><th>렌즈</th><th>결과</th><th>설명</th><th>근거</th></tr></thead><tbody>{% for row in improvement.lensCoverage %}<tr><td>{{ row.lens }}</td><td>{{ row.status }}</td><td>{{ row.rationale | inline_code }}</td><td>{{ row.evidence | evidence_refs(evidenceIndex) }}</td></tr>{% endfor %}</tbody></table>
|
|
10
10
|
</section>
|
|
11
11
|
|
|
12
12
|
<section data-report-section="ranked-candidates">
|
|
13
13
|
<h2>우선순위 후보</h2>
|
|
14
|
-
{{ render_narrative(narrative.prioritizationExplanation, "improvementDiscovery.userNarrative.prioritizationExplanation") }}
|
|
15
|
-
<div class="summary-grid" data-report-field="improvementDiscovery.candidates">{% for row in improvement.candidates %}<article class="summary-card tone-{{ row.severity }}"><p class="eyebrow">{{ loop.index }}순위 · {{ row.id }} · 영향 {{ row.severity }} · 노력 {{ row.effort }}</p><h3>{{ row.title }}</h3><p>{{ row.expectedBehaviorAfter }}</p><p><strong>다음 단계:</strong> {{ row.recommendedNextPhase }}</p><p class="evidence-refs">근거: {{ row.evidence |
|
|
14
|
+
{{ render_narrative(narrative.prioritizationExplanation, "improvementDiscovery.userNarrative.prioritizationExplanation", evidenceIndex) }}
|
|
15
|
+
<div class="summary-grid" data-report-field="improvementDiscovery.candidates">{% for row in improvement.candidates %}<article class="summary-card tone-{{ row.severity }}"><p class="eyebrow">{{ loop.index }}순위 · {{ row.id }} · 영향 {{ row.severity }} · 노력 {{ row.effort }}</p><h3>{{ row.title | inline_code }}</h3><p>{{ row.expectedBehaviorAfter | inline_code }}</p><p><strong>다음 단계:</strong> {{ row.recommendedNextPhase }}</p><p class="evidence-refs">근거: {{ row.evidence | evidence_refs(evidenceIndex) }}</p></article>{% else %}<p>선택할 개선 후보가 없습니다.</p>{% endfor %}</div>
|
|
16
16
|
</section>
|
|
17
17
|
|
|
18
18
|
<section data-report-section="impact-effort">
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
|
|
23
23
|
<section data-report-section="selection">
|
|
24
24
|
<h2>선택 안내</h2>
|
|
25
|
-
{{ render_narrative(narrative.selectionGuidance, "improvementDiscovery.userNarrative.selectionGuidance") }}
|
|
25
|
+
{{ render_narrative(narrative.selectionGuidance, "improvementDiscovery.userNarrative.selectionGuidance", evidenceIndex) }}
|
|
26
26
|
<p><strong>선택 상한:</strong> 최대 {{ improvement.selectionLimit }}개</p>
|
|
27
|
-
<ul>{% for row in improvement.candidates %}<li><label><input type="checkbox" name="improvement-candidate" value="{{ row.id }}"> {{ row.id }} · {{ row.title }}</label></li>{% endfor %}</ul>
|
|
27
|
+
<ul>{% for row in improvement.candidates %}<li><label><input type="checkbox" name="improvement-candidate" value="{{ row.id }}"> {{ row.id }} · {{ row.title | inline_code }}</label></li>{% endfor %}</ul>
|
|
28
28
|
</section>
|
|
29
29
|
{% endblock %}
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
{% block human_content %}
|
|
6
6
|
<section data-report-section="project-overview">
|
|
7
7
|
<h2>프로젝트 개요</h2>
|
|
8
|
-
{{ render_narrative(narrative.projectOverview, "projectAnalysis.userNarrative.projectOverview") }}
|
|
8
|
+
{{ render_narrative(narrative.projectOverview, "projectAnalysis.userNarrative.projectOverview", evidenceIndex) }}
|
|
9
9
|
<div class="summary-grid">
|
|
10
|
-
{% for reason in humanSummary.whyItMatters %}{{ summary_card("왜 중요한가", reason, "important") }}{% endfor %}
|
|
10
|
+
{% for reason in humanSummary.whyItMatters %}{{ summary_card("왜 중요한가 " ~ loop.index, reason, "important") }}{% endfor %}
|
|
11
11
|
</div>
|
|
12
12
|
</section>
|
|
13
13
|
|
|
14
14
|
<section data-report-section="architecture" data-report-field="projectAnalysis.components">
|
|
15
15
|
<h2>아키텍처와 의존성</h2>
|
|
16
|
-
{{ render_narrative(narrative.architectureExplanation, "projectAnalysis.userNarrative.architectureExplanation") }}
|
|
16
|
+
{{ render_narrative(narrative.architectureExplanation, "projectAnalysis.userNarrative.architectureExplanation", evidenceIndex) }}
|
|
17
17
|
{{ figure(architectureFigure) }}
|
|
18
18
|
</section>
|
|
19
19
|
|
|
@@ -23,35 +23,35 @@
|
|
|
23
23
|
{% for row in project.entryPoints %}{{ summary_card(row.symbol, row.role) }}{% endfor %}
|
|
24
24
|
</div>
|
|
25
25
|
<h3>데이터 저장소</h3>
|
|
26
|
-
<ul data-report-field="projectAnalysis.dataStores">{% for row in project.dataStores %}<li><strong>{{ row.name }}</strong> — {{ row.readBoundary }} / {{ row.writeBoundary }}</li>{% endfor %}</ul>
|
|
26
|
+
<ul data-report-field="projectAnalysis.dataStores">{% for row in project.dataStores %}<li><strong>{{ row.name | inline_code }}</strong> — {{ row.readBoundary | inline_code }} / {{ row.writeBoundary | inline_code }}</li>{% endfor %}</ul>
|
|
27
27
|
<h3>외부 시스템</h3>
|
|
28
|
-
<ul data-report-field="projectAnalysis.externalSystems">{% for row in project.externalSystems %}<li><strong>{{ row.name }}</strong> — {{ row.adapter }} ({{ row.direction }})</li>{% endfor %}</ul>
|
|
28
|
+
<ul data-report-field="projectAnalysis.externalSystems">{% for row in project.externalSystems %}<li><strong>{{ row.name | inline_code }}</strong> — {{ row.adapter | inline_code }} ({{ row.direction | inline_code }})</li>{% endfor %}</ul>
|
|
29
29
|
</section>
|
|
30
30
|
|
|
31
31
|
<section data-report-section="hotspots" data-report-field="projectAnalysis.rankedHotspots">
|
|
32
32
|
<h2>우선 검토할 핫스팟</h2>
|
|
33
|
-
{{ render_narrative(narrative.hotspotExplanation, "projectAnalysis.userNarrative.hotspotExplanation") }}
|
|
33
|
+
{{ render_narrative(narrative.hotspotExplanation, "projectAnalysis.userNarrative.hotspotExplanation", evidenceIndex) }}
|
|
34
34
|
<div class="summary-grid">
|
|
35
35
|
{% for row in project.rankedHotspots %}
|
|
36
|
-
<article class="summary-card tone-{{ row.risk }}"><p class="eyebrow">{{ row.id }} · 영향 {{ row.impact }} · 위험 {{ row.risk }}</p><h3>{{ row.title }}</h3><p>{{ row.reason }}</p><p class="evidence-refs">근거: {{ row.evidenceRefs |
|
|
36
|
+
<article class="summary-card tone-{{ row.risk }}"><p class="eyebrow">{{ row.id }} · 영향 {{ row.impact }} · 위험 {{ row.risk }}</p><h3>{{ row.title | inline_code }}</h3><p>{{ row.reason | inline_code }}</p><p class="evidence-refs">근거: {{ row.evidenceRefs | evidence_refs(evidenceIndex) }}</p></article>
|
|
37
37
|
{% endfor %}
|
|
38
38
|
</div>
|
|
39
39
|
</section>
|
|
40
40
|
|
|
41
41
|
<section data-report-section="quality-and-tests" data-report-field="projectAnalysis.qualityCoverage">
|
|
42
42
|
<h2>품질과 테스트 상태</h2>
|
|
43
|
-
{{ render_narrative(narrative.qualityExplanation, "projectAnalysis.userNarrative.qualityExplanation") }}
|
|
44
|
-
<table><thead><tr><th>영역</th><th>상태</th><th>설명</th><th>근거</th></tr></thead><tbody>{% for row in project.qualityCoverage %}<tr><td>{{ row.area }}</td><td><span class="status status-{{ row.status }}">{{ row.status }}</span></td><td>{{ row.summary }}</td><td>{{ row.evidenceRefs |
|
|
43
|
+
{{ render_narrative(narrative.qualityExplanation, "projectAnalysis.userNarrative.qualityExplanation", evidenceIndex) }}
|
|
44
|
+
<table><thead><tr><th>영역</th><th>상태</th><th>설명</th><th>근거</th></tr></thead><tbody>{% for row in project.qualityCoverage %}<tr><td>{{ row.area | inline_code }}</td><td><span class="status status-{{ row.status }}">{{ row.status }}</span></td><td>{{ row.summary | inline_code }}</td><td>{{ row.evidenceRefs | evidence_refs(evidenceIndex) }}</td></tr>{% endfor %}</tbody></table>
|
|
45
45
|
</section>
|
|
46
46
|
|
|
47
47
|
<section data-report-section="feature-map" data-report-field="projectAnalysis.featureIndex">
|
|
48
48
|
<h2>기능 지도</h2>
|
|
49
|
-
<div class="summary-grid">{% for row in project.featureIndex %}{{ summary_card(row.id ~ " · " ~ row.name, row.description) }}{% endfor %}</div>
|
|
49
|
+
<div class="summary-grid">{% for row in project.featureIndex %}{{ summary_card(row.id ~ " · " ~ (row.name | inline_code), row.description) }}{% endfor %}</div>
|
|
50
50
|
</section>
|
|
51
51
|
|
|
52
52
|
<section data-report-section="user-actions">
|
|
53
53
|
<h2>다음 행동</h2>
|
|
54
54
|
{{ action_list(humanSummary.actions) }}
|
|
55
|
-
{% if humanSummary.blockers %}<h3>차단 사항</h3><ul>{% for item in humanSummary.blockers %}<li>{{ item }}</li>{% endfor %}</ul>{% endif %}
|
|
55
|
+
{% if humanSummary.blockers %}<h3>차단 사항</h3><ul>{% for item in humanSummary.blockers %}<li>{{ item | inline_code }}</li>{% endfor %}</ul>{% endif %}
|
|
56
56
|
</section>
|
|
57
57
|
{% endblock %}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
{% block human_content %}
|
|
6
6
|
<section data-report-section="handoff-outcome">
|
|
7
7
|
<h2>인계 결과</h2>
|
|
8
|
-
{{ render_narrative(narrative.handoffOutcome, "releaseHandoff.userNarrative.handoffOutcome") }}
|
|
8
|
+
{{ render_narrative(narrative.handoffOutcome, "releaseHandoff.userNarrative.handoffOutcome", evidenceIndex) }}
|
|
9
9
|
{{ figure(handoffFigure) }}
|
|
10
10
|
</section>
|
|
11
11
|
|
|
@@ -16,21 +16,21 @@
|
|
|
16
16
|
|
|
17
17
|
<section data-report-section="branch-and-pr" data-report-field="releaseHandoff.featureBranchState">
|
|
18
18
|
<h2>브랜치와 PR</h2>
|
|
19
|
-
{{ render_narrative(narrative.branchAndPrExplanation, "releaseHandoff.userNarrative.branchAndPrExplanation") }}
|
|
19
|
+
{{ render_narrative(narrative.branchAndPrExplanation, "releaseHandoff.userNarrative.branchAndPrExplanation", evidenceIndex) }}
|
|
20
20
|
<p><strong>브랜치:</strong> {{ handoff.featureBranchState.branchName }}</p>
|
|
21
21
|
<p data-report-field="releaseHandoff.pullRequestOutcome"><strong>PR 결과:</strong> {{ handoff.pullRequestOutcome.kind }}{% if prUrl %} · <a href="{{ prUrl }}" rel="noopener noreferrer">Pull request 열기</a>{% endif %}</p>
|
|
22
22
|
</section>
|
|
23
23
|
|
|
24
24
|
<section data-report-section="conflicts" data-report-field="releaseHandoff.mergeConflictProbe">
|
|
25
25
|
<h2>충돌 판정</h2>
|
|
26
|
-
{{ render_narrative(narrative.conflictExplanation, "releaseHandoff.userNarrative.conflictExplanation") }}
|
|
26
|
+
{{ render_narrative(narrative.conflictExplanation, "releaseHandoff.userNarrative.conflictExplanation", evidenceIndex) }}
|
|
27
27
|
{{ summary_card(handoff.mergeConflictProbe.kind, "기준 브랜치: " ~ (handoff.mergeConflictProbe.baseBranch | default('not-run'))) }}
|
|
28
28
|
{% if handoff.mergeConflictProbe.conflictingPaths %}<ul>{% for path in handoff.mergeConflictProbe.conflictingPaths %}<li>{{ path }}</li>{% endfor %}</ul>{% endif %}
|
|
29
29
|
</section>
|
|
30
30
|
|
|
31
31
|
<section data-report-section="next-action">
|
|
32
32
|
<h2>다음 행동</h2>
|
|
33
|
-
{{ render_narrative(narrative.nextAction, "releaseHandoff.userNarrative.nextAction") }}
|
|
34
|
-
<p>{{ handoff.routingRecommendation }}</p>
|
|
33
|
+
{{ render_narrative(narrative.nextAction, "releaseHandoff.userNarrative.nextAction", evidenceIndex) }}
|
|
34
|
+
<p>{{ handoff.routingRecommendation | inline_code }}</p>
|
|
35
35
|
</section>
|
|
36
36
|
{% endblock %}
|
|
@@ -5,26 +5,26 @@
|
|
|
5
5
|
{% block human_content %}
|
|
6
6
|
<section data-report-section="request">
|
|
7
7
|
<h2>요청 이해</h2>
|
|
8
|
-
{{ render_narrative(narrative.requestUnderstanding, "requirementsDiscovery.userNarrative.requestUnderstanding") }}
|
|
8
|
+
{{ render_narrative(narrative.requestUnderstanding, "requirementsDiscovery.userNarrative.requestUnderstanding", evidenceIndex) }}
|
|
9
9
|
<h3>사용자 요청 원문</h3>
|
|
10
|
-
<p data-report-field="requirementsDiscovery.requestVerbatim">{{ requirements.requestVerbatim }}</p>
|
|
10
|
+
<p data-report-field="requirementsDiscovery.requestVerbatim">{{ requirements.requestVerbatim | inline_code }}</p>
|
|
11
11
|
<h3>시스템 해석</h3>
|
|
12
|
-
<p data-report-field="requirementsDiscovery.systemInterpretation">{{ requirements.systemInterpretation }}</p>
|
|
13
|
-
<dl data-report-field="requirementsDiscovery.classification">{% for key, value in requirements.classification.items() %}<dt>{{ key }}</dt><dd>{{ value }}</dd>{% endfor %}</dl>
|
|
12
|
+
<p data-report-field="requirementsDiscovery.systemInterpretation">{{ requirements.systemInterpretation | inline_code }}</p>
|
|
13
|
+
<dl data-report-field="requirementsDiscovery.classification">{% for key, value in requirements.classification.items() %}<dt>{{ key }}</dt><dd>{{ value | inline_code }}</dd>{% endfor %}</dl>
|
|
14
14
|
</section>
|
|
15
15
|
|
|
16
16
|
<section data-report-section="requirements">
|
|
17
17
|
<h2>확정 요구사항과 미확정 요구사항</h2>
|
|
18
|
-
{{ render_narrative(narrative.confirmedRequirements, "requirementsDiscovery.userNarrative.confirmedRequirements") }}
|
|
18
|
+
{{ render_narrative(narrative.confirmedRequirements, "requirementsDiscovery.userNarrative.confirmedRequirements", evidenceIndex) }}
|
|
19
19
|
<div class="summary-grid" data-report-field="requirementsDiscovery.confirmedRequirements">{% for row in requirements.confirmedRequirements %}{{ summary_card(row.id ~ " · 확정", row.statement) }}{% endfor %}</div>
|
|
20
|
-
{{ render_narrative(narrative.unresolvedRequirements, "requirementsDiscovery.userNarrative.unresolvedRequirements") }}
|
|
20
|
+
{{ render_narrative(narrative.unresolvedRequirements, "requirementsDiscovery.userNarrative.unresolvedRequirements", evidenceIndex) }}
|
|
21
21
|
<div class="summary-grid" data-report-field="requirementsDiscovery.unresolvedRequirements">{% for row in requirements.unresolvedRequirements %}{{ summary_card(row.id ~ " · 결정 필요", row.statement, "important") }}{% else %}<p>미확정 요구사항이 없습니다.</p>{% endfor %}</div>
|
|
22
22
|
</section>
|
|
23
23
|
|
|
24
24
|
<section data-report-section="routing">
|
|
25
25
|
<h2>다음 태스크와 의존성</h2>
|
|
26
|
-
{{ render_narrative(narrative.routingExplanation, "requirementsDiscovery.userNarrative.routingExplanation") }}
|
|
27
|
-
<p><strong>다음 태스크:</strong> {{ requirements.routing.nextTaskType }} · {{ requirements.routing.rationale }}</p>
|
|
26
|
+
{{ render_narrative(narrative.routingExplanation, "requirementsDiscovery.userNarrative.routingExplanation", evidenceIndex) }}
|
|
27
|
+
<p><strong>다음 태스크:</strong> {{ requirements.routing.nextTaskType }} · {{ requirements.routing.rationale | inline_code }}</p>
|
|
28
28
|
{{ figure(routingFigure) }}
|
|
29
29
|
{% if not requirements.taskDependencies %}<p class="empty-state">이 요구사항 태스크는 다른 태스크에 의존하지 않는 단일 작업입니다.</p>{% endif %}
|
|
30
30
|
</section>
|
|
@@ -249,6 +249,16 @@
|
|
|
249
249
|
setTimeout(function () { URL.revokeObjectURL(url); }, 0);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
// The output element lives in the footer, many screens below the header
|
|
253
|
+
// export button, so writing text into it alone reads as "nothing happened".
|
|
254
|
+
function showOutput(out, text) {
|
|
255
|
+
if (!out) return;
|
|
256
|
+
out.textContent = text;
|
|
257
|
+
out.setAttribute("tabindex", "-1");
|
|
258
|
+
out.scrollIntoView({ block: "center" });
|
|
259
|
+
out.focus();
|
|
260
|
+
}
|
|
261
|
+
|
|
252
262
|
function exportUserResponse() {
|
|
253
263
|
var runMeta = readRunMeta();
|
|
254
264
|
var entries = collectEntries();
|
|
@@ -258,14 +268,14 @@
|
|
|
258
268
|
try {
|
|
259
269
|
analysisReview = collectAnalysisReview();
|
|
260
270
|
} catch (e) {
|
|
261
|
-
|
|
271
|
+
showOutput(out, e.message || String(e));
|
|
262
272
|
showDismiss(true);
|
|
263
273
|
return "";
|
|
264
274
|
}
|
|
265
275
|
var md = buildUserResponseMarkdown(
|
|
266
276
|
runMeta, entries, isoNowUtc(), approval, analysisReview
|
|
267
277
|
);
|
|
268
|
-
|
|
278
|
+
showOutput(out, md);
|
|
269
279
|
showDismiss(true);
|
|
270
280
|
// Nothing answered yet — show the empty serialisation as feedback but
|
|
271
281
|
// don't download a useless file.
|
|
@@ -315,7 +325,10 @@
|
|
|
315
325
|
if (/Reader Summary|Verdict Card|Clarification Items|Recommended Next Steps|Follow-up Tasks/.test(text)) {
|
|
316
326
|
return "action";
|
|
317
327
|
}
|
|
318
|
-
|
|
328
|
+
// "토큰 사용량" is tokenSummary.heading from templates/reports/i18n/ko.json —
|
|
329
|
+
// the only audit heading the ko dictionary translates away from its English
|
|
330
|
+
// form. tests/report/test_report_views.py pins the two in sync.
|
|
331
|
+
if (/Evidence|Cross Verification|Execution Status|Token Usage|토큰 사용량|Plan Body Verification|Round History/.test(text)) {
|
|
319
332
|
return "audit";
|
|
320
333
|
}
|
|
321
334
|
return "detail";
|
|
@@ -333,7 +346,11 @@
|
|
|
333
346
|
current = "action";
|
|
334
347
|
continue;
|
|
335
348
|
}
|
|
336
|
-
|
|
349
|
+
// Only h2 opens a reader section. The single h1 is the report title, and
|
|
350
|
+
// the run-metadata list and table of contents that trail it are document
|
|
351
|
+
// chrome — classifying them would hide the title in action mode, because
|
|
352
|
+
// the title text ("… Cross Verification Final Report") reads as audit.
|
|
353
|
+
if ((node.tagName || "") === "H2") {
|
|
337
354
|
current = readerSectionKind(node.textContent || "");
|
|
338
355
|
}
|
|
339
356
|
node.setAttribute("data-reader-section", current);
|
|
@@ -131,8 +131,8 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
131
131
|
- Should the same task key continue to be used after this run?
|
|
132
132
|
- What is the likely work category for this task right now?
|
|
133
133
|
- Which lifecycle phase should probably happen next after this run?
|
|
134
|
-
- What prior decision or report should
|
|
135
|
-
- What related tasks should
|
|
134
|
+
- What prior decision or report should the Okstra lead compare against?
|
|
135
|
+
- What related tasks should the Okstra lead keep in mind but not merge with this task?
|
|
136
136
|
- Which config files and deployment manifests carry the expected state for this task?
|
|
137
137
|
|
|
138
138
|
## Phase Boundary
|
|
@@ -145,27 +145,27 @@ taskType: "{{FM_TASK_TYPE}}"
|
|
|
145
145
|
|
|
146
146
|
## Available MCP Servers
|
|
147
147
|
|
|
148
|
-
The MCP servers available to this run are declared in `.okstra/project.json`'s `mcpServers` array and rendered into the
|
|
148
|
+
The MCP servers available to this run are declared in `.okstra/project.json`'s `mcpServers` array and rendered into the Okstra lead's launch prompt under `## Available MCP Servers`. The lead is responsible for forwarding the rendered list verbatim into worker prompts (Phase 2) so each assignment can determine whether its selected runner exposes the named tools.
|
|
149
149
|
|
|
150
150
|
To declare servers, add entries shaped `{ "name": "<server>", "description": "...", "tools": ["..."], "notes": "..." }` to that array. If the array is empty or absent, treat MCP as unavailable for this run.
|
|
151
151
|
|
|
152
152
|
How to invoke (worker-by-worker):
|
|
153
153
|
|
|
154
|
-
- **
|
|
155
|
-
- **
|
|
154
|
+
- **Native-session assignments**: invoke an MCP tool directly by its tool name (e.g. `mcp__<server>__<tool>`) only when the selected host adapter exposes it. Do not call an MCP tool name through a shell.
|
|
155
|
+
- **CLI-wrapper assignments**: use only the MCP or tool configuration explicitly supported and forwarded by that provider wrapper. If the wrapper has no matching MCP configuration, record `MCP not available in this CLI` in `Missing Information or Assumptions`; do not attempt a shell fallback.
|
|
156
156
|
- All workers: cite the exact server, tool, and SELECT (or `WHERE` filters) used in the result file. Tool-call failures must be logged in the worker's `*-errors.json` (commandKind `mcp_call`) so the lead can decide whether to retry under a different worker.
|
|
157
157
|
|
|
158
158
|
Usage policy:
|
|
159
159
|
|
|
160
160
|
- **Allowed phases**: `requirements-discovery`, `error-analysis`, `implementation-planning`, `final-verification`. Use only when local schema/data evidence improves the answer. Always cite the server, table, and the SELECT used as evidence in worker output.
|
|
161
161
|
- **`implementation` phase**: read-only MCP queries are permitted as cross-checks; MCP MUST NOT be used as a write path even if a write tool becomes available — schema/data mutations belong in the codebase migration files reviewed by humans.
|
|
162
|
-
- **
|
|
162
|
+
- **CLI-wrapper workers**: can use these MCP servers only if their provider CLI configuration mirrors the same servers. If not configured, the worker should record `MCP not available in this CLI` in `Missing Information or Assumptions` rather than guessing.
|
|
163
163
|
- **Forbidden**: connecting to non-listed databases, running anything that mutates state (server is read-only — flagged write attempts are a contract violation), persisting query results outside the run's own artifact directories.
|
|
164
164
|
- **Limits**: `maxRows: 1000` per query (server-enforced). For larger scans, page via `WHERE` predicates and document the strategy.
|
|
165
165
|
|
|
166
166
|
If MCP evidence contradicts a claim sourced from documentation, code, or prior reports, flag the conflict explicitly in the final report and prefer the live MCP evidence unless the brief says otherwise.
|
|
167
167
|
|
|
168
|
-
## Notes for
|
|
168
|
+
## Notes for Okstra Lead
|
|
169
169
|
|
|
170
170
|
- Which workers should be used?
|
|
171
171
|
- Should all workers receive the same brief or different emphasis?
|
|
@@ -51,6 +51,7 @@ from okstra_ctl.conformance import ( # noqa: E402
|
|
|
51
51
|
validate_conformance_manifest,
|
|
52
52
|
)
|
|
53
53
|
from okstra_ctl.paths import RunRef # noqa: E402
|
|
54
|
+
from okstra_ctl.lead_runtime import ARTIFACT_ONLY_LEAD_RUNTIMES # noqa: E402
|
|
54
55
|
from okstra_ctl.build_tools import ( # noqa: E402
|
|
55
56
|
command_invokes_build_tool,
|
|
56
57
|
resolve_build_tool_tokens,
|
|
@@ -156,7 +157,7 @@ def _session_accounting(team_state: dict) -> str:
|
|
|
156
157
|
return value
|
|
157
158
|
return (
|
|
158
159
|
"artifact-only"
|
|
159
|
-
if team_state.get("leadRuntime") in
|
|
160
|
+
if team_state.get("leadRuntime") in ARTIFACT_ONLY_LEAD_RUNTIMES
|
|
160
161
|
else "claude-jsonl"
|
|
161
162
|
)
|
|
162
163
|
|
|
@@ -415,20 +416,24 @@ def extract_contract(
|
|
|
415
416
|
required_worker_roles = []
|
|
416
417
|
failures.append("requiredWorkerRoles is missing from run/task manifest")
|
|
417
418
|
|
|
419
|
+
lead_role = (
|
|
420
|
+
run_contract.get("leadRole")
|
|
421
|
+
or task_contract.get("leadRole")
|
|
422
|
+
or "Okstra lead"
|
|
423
|
+
)
|
|
424
|
+
|
|
418
425
|
required_agent_status_entries = run_contract.get("requiredAgentStatusEntries")
|
|
419
426
|
if not isinstance(required_agent_status_entries, list):
|
|
420
427
|
required_agent_status_entries = task_contract.get("requiredAgentStatusEntries")
|
|
421
428
|
if not isinstance(required_agent_status_entries, list):
|
|
422
|
-
required_agent_status_entries = [
|
|
429
|
+
required_agent_status_entries = [lead_role] + [
|
|
423
430
|
item.get("role", "")
|
|
424
431
|
for item in required_worker_roles
|
|
425
432
|
if isinstance(item, dict) and item.get("role")
|
|
426
433
|
]
|
|
427
434
|
|
|
428
435
|
return {
|
|
429
|
-
"lead_role":
|
|
430
|
-
or task_contract.get("leadRole")
|
|
431
|
-
or "Claude lead",
|
|
436
|
+
"lead_role": lead_role,
|
|
432
437
|
"lead_agent": run_contract.get("leadAgent")
|
|
433
438
|
or task_contract.get("leadAgent")
|
|
434
439
|
or "claude",
|
|
@@ -722,7 +727,7 @@ def validate_team_state(
|
|
|
722
727
|
dispatch_mode = str(team_state.get("dispatchMode", "")).strip()
|
|
723
728
|
if (
|
|
724
729
|
dispatch_mode in WORKER_DISPATCH_MODES
|
|
725
|
-
or team_state.get("leadRuntime") in
|
|
730
|
+
or team_state.get("leadRuntime") in ARTIFACT_ONLY_LEAD_RUNTIMES
|
|
726
731
|
):
|
|
727
732
|
if dispatch_mode not in WORKER_DISPATCH_MODES:
|
|
728
733
|
expected = ", ".join(sorted(WORKER_DISPATCH_MODES))
|
|
@@ -38,6 +38,7 @@ from okstra_ctl.worker_heartbeat import ( # noqa: E402
|
|
|
38
38
|
HEARTBEAT_LINE_RE,
|
|
39
39
|
max_gap_seconds_after,
|
|
40
40
|
)
|
|
41
|
+
from okstra_ctl.lead_runtime import ARTIFACT_ONLY_LEAD_RUNTIMES # noqa: E402
|
|
41
42
|
from okstra_ctl.wrapper_status import read_wrapper_status # noqa: E402
|
|
42
43
|
|
|
43
44
|
_DISPATCHED_STATUSES = {"completed", "timeout", "error", "in-progress"}
|
|
@@ -366,7 +367,7 @@ def _conformance_evidence_source(
|
|
|
366
367
|
return None, f"unsupported sessionAccounting: `{accounting}`"
|
|
367
368
|
|
|
368
369
|
legacy_runtime = str(team_state.get("leadRuntime", "") or "claude-code")
|
|
369
|
-
if legacy_runtime in
|
|
370
|
+
if legacy_runtime in ARTIFACT_ONLY_LEAD_RUNTIMES:
|
|
370
371
|
return "artifact-only", None
|
|
371
372
|
return "claude-jsonl", None
|
|
372
373
|
|
package/src/cli-registry.mjs
CHANGED
|
@@ -415,18 +415,18 @@ export const COMMANDS = new Map(
|
|
|
415
415
|
]),
|
|
416
416
|
);
|
|
417
417
|
|
|
418
|
-
const PREAMBLE = `okstra — multi-
|
|
418
|
+
const PREAMBLE = `okstra — host-aware multi-provider cross-verification orchestrator
|
|
419
419
|
|
|
420
420
|
This CLI is the installer/admin tool. Day-to-day usage happens inside a
|
|
421
|
-
|
|
421
|
+
supported agent host through installed skills (okstra-setup, okstra-run, ...).
|
|
422
422
|
|
|
423
423
|
Quick start (CLI):
|
|
424
424
|
1. npx -y okstra@latest install # one-time, this machine
|
|
425
425
|
2. cd <your project>
|
|
426
426
|
3. npx -y okstra@latest setup --project-id <id> # one-time, this project
|
|
427
|
-
4. open
|
|
427
|
+
4. open Claude Code, Codex, or Antigravity and run okstra-run # start a task
|
|
428
428
|
|
|
429
|
-
Inside a
|
|
429
|
+
Inside a supported agent host you can replace step 3 with okstra-setup —
|
|
430
430
|
both write the same <PROJECT_ROOT>/.okstra/project.json.
|
|
431
431
|
|
|
432
432
|
Usage:
|
|
@@ -5,18 +5,15 @@ const USAGE = `okstra codex-dispatch — dispatch CLI-backed workers for a prepa
|
|
|
5
5
|
|
|
6
6
|
Usage:
|
|
7
7
|
okstra codex-dispatch --project-root <dir> --run-manifest <path> \\
|
|
8
|
-
[--workers
|
|
9
|
-
[--idle-timeout-seconds <n>]
|
|
10
|
-
[--enable-codex-report-writer --report-writer-codex-model <model>]
|
|
8
|
+
[--workers <cli-assigned-worker-ids>] [--dry-run] \\
|
|
9
|
+
[--idle-timeout-seconds <n>]
|
|
11
10
|
|
|
12
11
|
This command reads an existing run manifest produced by 'okstra codex-run' and
|
|
13
|
-
dispatches only
|
|
14
|
-
|
|
15
|
-
report-writer
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
writer model by default. Explicitly requesting unsupported workers such as
|
|
19
|
-
claude fails.
|
|
12
|
+
dispatches only assignments whose persisted runner is 'cli-wrapper'. When
|
|
13
|
+
--workers is omitted, it selects that subset from the run roster. Each worker,
|
|
14
|
+
including report-writer, uses its persisted provider, model, and registered
|
|
15
|
+
wrapper. Native-session assignments remain owned by the current Codex host;
|
|
16
|
+
explicitly requesting one here fails.
|
|
20
17
|
|
|
21
18
|
Missing worker prompt files are generated automatically from the run manifest
|
|
22
19
|
and active-run-context. Existing prompt files are never overwritten.
|
|
@@ -2,7 +2,7 @@ import { runPythonModule } from "../../lib/python-helper.mjs";
|
|
|
2
2
|
import { resolvePaths } from "../../lib/paths.mjs";
|
|
3
3
|
import { resolveRuntime } from "../../lib/runtime-resolver.mjs";
|
|
4
4
|
|
|
5
|
-
const USAGE = `okstra render-bundle — preview the task bundle without launching
|
|
5
|
+
const USAGE = `okstra render-bundle — preview the task bundle without launching a lead
|
|
6
6
|
|
|
7
7
|
This is a thin shim over \`python3 -m okstra_ctl.run\` that forces
|
|
8
8
|
\`--render-only\`. Use it to preview where prepare_task_bundle() would
|
|
@@ -18,7 +18,7 @@ Usage:
|
|
|
18
18
|
[--analysis-target <text>] [--evidence-inputs <csv>] \\
|
|
19
19
|
[--lead-model <m>] [--claude-model <m>] [--codex-model <m>] \\
|
|
20
20
|
[--antigravity-model <m>] [--report-writer-model <m>] \\
|
|
21
|
-
[--lead-runtime claude-code|codex] \\
|
|
21
|
+
[--lead-runtime claude-code|codex|antigravity|external] \\
|
|
22
22
|
[--related-tasks <list>] [--base-ref <ref>] \\
|
|
23
23
|
[--clarification-response <path>] [--work-category <cat>] \\
|
|
24
24
|
[--stage <auto|N>] [--stages <csv>] [--pr-template-path <path>] \\
|
|
@@ -26,7 +26,7 @@ Usage:
|
|
|
26
26
|
|
|
27
27
|
--stage implementation / final-verification only
|
|
28
28
|
--stages release-handoff only: PR stage bundle csv (empty = whole-task)
|
|
29
|
-
--lead-runtime lead adapter marker
|
|
29
|
+
--lead-runtime host-native lead adapter marker
|
|
30
30
|
--pr-template-path release-handoff only
|
|
31
31
|
--fix-cycle entry phase only: record this re-entry of a done task as a
|
|
32
32
|
bug-fix cycle (yes opens/continues the cycle)
|