okstra 0.200.0 → 0.201.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 +4 -2
- package/dist/cli-registry.mjs +6 -0
- package/dist/cli-registry.mjs.map +1 -1
- package/docs/cli.md +14 -3
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/report-writer-worker.md +7 -3
- package/runtime/bin/okstra-spawn-followups.py +2 -2
- package/runtime/prompts/duties/technical-verification-worker.md +44 -0
- package/runtime/prompts/launch.template.md +7 -1
- package/runtime/prompts/lead/okstra-lead-contract.md +7 -2
- package/runtime/prompts/lead/plan-body-verification.md +3 -1
- package/runtime/prompts/lead/report-writer.md +11 -5
- package/runtime/prompts/lead/team-contract.md +6 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +7 -1
- package/runtime/prompts/profiles/final-verification.md +5 -0
- package/runtime/prompts/profiles/forbidden-actions.json +6 -0
- package/runtime/prompts/profiles/implementation-option-selection.md +7 -1
- package/runtime/prompts/profiles/implementation-planning.md +1 -0
- package/runtime/prompts/profiles/technical-verification.md +53 -0
- package/runtime/prompts/wizard/prompts.ko.json +2 -1
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -4
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +2 -0
- package/runtime/python/okstra_ctl/adapters/providers/zai/adapter.py +36 -5
- package/runtime/python/okstra_ctl/agent/invocation.py +14 -6
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +4 -3
- package/runtime/python/okstra_ctl/agent/prompt_cli/corrections.py +83 -22
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +44 -2
- package/runtime/python/okstra_ctl/conformance.py +2 -20
- package/runtime/python/okstra_ctl/dispatch_core.py +25 -5
- package/runtime/python/okstra_ctl/dispatch_state.py +2 -0
- package/runtime/python/okstra_ctl/domain/provider.py +0 -1
- package/runtime/python/okstra_ctl/domain/role.py +1 -0
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +6 -1
- package/runtime/python/okstra_ctl/implementation_direction.py +64 -7
- package/runtime/python/okstra_ctl/implementation_options.py +58 -45
- package/runtime/python/okstra_ctl/model_pool.py +2 -5
- package/runtime/python/okstra_ctl/next_phase.py +3 -0
- package/runtime/python/okstra_ctl/plan_items.py +15 -0
- package/runtime/python/okstra_ctl/plan_items_cli.py +9 -3
- package/runtime/python/okstra_ctl/qa_commands.py +30 -0
- package/runtime/python/okstra_ctl/registry/provider_registry.py +11 -8
- package/runtime/python/okstra_ctl/render.py +3 -0
- package/runtime/python/okstra_ctl/render_final_report.py +1 -0
- package/runtime/python/okstra_ctl/report_assembly.py +8 -2
- package/runtime/python/okstra_ctl/report_contract.py +3 -0
- package/runtime/python/okstra_ctl/report_corrections.py +209 -93
- package/runtime/python/okstra_ctl/report_finalize.py +25 -8
- package/runtime/python/okstra_ctl/report_html/router.py +2 -0
- package/runtime/python/okstra_ctl/report_html/view_models/technical_verification.py +21 -0
- package/runtime/python/okstra_ctl/report_projections.py +4 -3
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +181 -47
- package/runtime/python/okstra_ctl/run.py +82 -0
- package/runtime/python/okstra_ctl/team.py +4 -1
- package/runtime/python/okstra_ctl/technical_verification.py +195 -0
- package/runtime/python/okstra_ctl/usage_identity.py +54 -0
- package/runtime/python/okstra_ctl/usage_report.py +22 -8
- package/runtime/python/okstra_ctl/verification_target.py +74 -0
- package/runtime/python/okstra_ctl/wizard/__init__.py +1 -1
- package/runtime/python/okstra_ctl/wizard/cli.py +2 -1
- package/runtime/python/okstra_ctl/wizard/confirmation.py +38 -2
- package/runtime/python/okstra_ctl/wizard/engine.py +3 -0
- package/runtime/python/okstra_ctl/wizard/ids.py +1 -0
- package/runtime/python/okstra_ctl/wizard/outcome.py +63 -0
- package/runtime/python/okstra_ctl/wizard/picker_navigation.py +2 -2
- package/runtime/python/okstra_ctl/wizard/registry.py +1 -1
- package/runtime/python/okstra_ctl/wizard/render.py +8 -55
- package/runtime/python/okstra_ctl/wizard/roles.py +11 -7
- package/runtime/python/okstra_ctl/wizard/sources.py +28 -2
- package/runtime/python/okstra_ctl/wizard/state.py +13 -6
- package/runtime/python/okstra_ctl/wizard/steps_plan.py +8 -0
- package/runtime/python/okstra_ctl/worker_liveness.py +52 -39
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +2 -0
- package/runtime/python/okstra_ctl/workflow.py +8 -0
- package/runtime/python/okstra_ctl/write_policy.py +23 -0
- package/runtime/python/okstra_token_usage/blocks.py +50 -1
- package/runtime/python/okstra_token_usage/claude.py +42 -21
- package/runtime/python/okstra_token_usage/codex.py +17 -0
- package/runtime/python/okstra_token_usage/collect.py +299 -162
- package/runtime/python/okstra_token_usage/cursor.py +2 -3
- package/runtime/python/okstra_token_usage/report.py +35 -30
- package/runtime/python/okstra_token_usage/task_totals.py +3 -12
- package/runtime/schemas/final-report-v2.0.schema.json +298 -7
- package/runtime/schemas/final-report-v3.0.schema.json +298 -7
- package/runtime/schemas/report-narrative-v3.0.schema.json +1 -0
- package/runtime/schemas/report-synthesis-packet-v1.0.schema.json +1 -1
- package/runtime/schemas/report-writer-corrections-v1.0.schema.json +30 -3
- package/runtime/skills/okstra-run/SKILL.md +10 -2
- package/runtime/skills/okstra-setup/SKILL.md +42 -7
- package/runtime/templates/report-writer-prompt-preamble.md +7 -3
- package/runtime/templates/reports/html/i18n/en.json +11 -0
- package/runtime/templates/reports/html/i18n/ko.json +11 -0
- package/runtime/templates/reports/html/tasks/implementation-option-selection.template.html +7 -3
- package/runtime/templates/reports/html/tasks/technical-verification.template.html +35 -0
- package/runtime/templates/reports/md/tasks/technical-verification.template.md +5 -0
- package/runtime/validators/validate-run.py +9 -4
|
@@ -446,6 +446,9 @@
|
|
|
446
446
|
"verdict": "Verdict",
|
|
447
447
|
"counterevidence": "Strongest counterevidence",
|
|
448
448
|
"safety-blockers": "Safety blockers",
|
|
449
|
+
"technical-verification": "Technical verification pending",
|
|
450
|
+
"user-decision": "User decision record",
|
|
451
|
+
"classification-required": "Classification required before reassembly",
|
|
449
452
|
"unresolved-facts": "Unresolved feasibility facts",
|
|
450
453
|
"expected-outcome": "Expected outcome",
|
|
451
454
|
"coverage-line": "Covers {covered} of {total} requirements ({verdict}).",
|
|
@@ -686,6 +689,14 @@
|
|
|
686
689
|
"decision-needed": "Decision needed",
|
|
687
690
|
"glossary": "Glossary",
|
|
688
691
|
"decision-record": "Decision record"
|
|
692
|
+
},
|
|
693
|
+
"technical-verification": {
|
|
694
|
+
"source": "Source comparison",
|
|
695
|
+
"scope": "Technical evidence only; no implementation direction is approved.",
|
|
696
|
+
"checks": "Experiment plans and observations",
|
|
697
|
+
"confirming": "Confirming signal",
|
|
698
|
+
"rejecting": "Rejecting signal",
|
|
699
|
+
"next": "Return to comparison"
|
|
689
700
|
}
|
|
690
701
|
}
|
|
691
702
|
}
|
|
@@ -446,6 +446,9 @@
|
|
|
446
446
|
"verdict": "판정",
|
|
447
447
|
"counterevidence": "가장 강한 반대 근거",
|
|
448
448
|
"safety-blockers": "안전 차단 요인",
|
|
449
|
+
"technical-verification": "기술 검증 대기",
|
|
450
|
+
"user-decision": "사용자 결정 기록",
|
|
451
|
+
"classification-required": "재조립 전 해결 방식 분류 필요",
|
|
449
452
|
"unresolved-facts": "미확정 실행 가능성 사실",
|
|
450
453
|
"expected-outcome": "예상 결과",
|
|
451
454
|
"coverage-line": "요구사항 {total}개 중 {covered}개를 충족합니다({verdict}).",
|
|
@@ -686,6 +689,14 @@
|
|
|
686
689
|
"decision-needed": "결정 필요",
|
|
687
690
|
"glossary": "용어집",
|
|
688
691
|
"decision-record": "결정 기록"
|
|
692
|
+
},
|
|
693
|
+
"technical-verification": {
|
|
694
|
+
"source": "검증 대상 비교 보고서",
|
|
695
|
+
"scope": "기술적 근거만 수집하며 구현 방향을 승인하지 않습니다.",
|
|
696
|
+
"checks": "시험 계획과 관측 결과",
|
|
697
|
+
"confirming": "가설을 지지하는 신호",
|
|
698
|
+
"rejecting": "가설을 반박하는 신호",
|
|
699
|
+
"next": "구현 방향 재비교"
|
|
689
700
|
}
|
|
690
701
|
}
|
|
691
702
|
}
|
|
@@ -33,8 +33,7 @@
|
|
|
33
33
|
<table data-report-field="implementationOptionSelection.rankedOptions.feasibilityVotes"><thead><tr><th>{{ t('tasks.implementation-option-selection.worker') }}</th><th>{{ t('tasks.implementation-option-selection.reason') }}</th><th>{{ t('tasks.implementation-option-selection.counterevidence') }}</th></tr></thead><tbody>{% for vote in row.feasibilityVotes %}<tr>{{ row_key(pairs=[(t('tasks.implementation-option-selection.worker'), vote.worker)], status_name=t('tasks.implementation-option-selection.verdict'), status_raw=vote.verdict, status_text=vote.verdict | enum_label('feasibilityVerdict')) }}<td>{{ vote.rationale | inline_code(extra) }}</td><td>{{ vote.counterevidence | inline_code(extra) }}</td></tr>{% endfor %}</tbody></table>
|
|
34
34
|
{% if row.safetyBlockers %}<p><strong>{{ t('tasks.implementation-option-selection.safety-blockers') }}</strong></p>
|
|
35
35
|
<ul>{% for blocker in row.safetyBlockers %}<li><span class="status status-risk">{{ blocker.category | enum_label('safetyCategory') }}</span> {{ blocker.statement | inline_code(extra) }} <span class="evidence-refs">{{ t('macros.layout.evidence') }} {{ blocker.evidence | evidence_refs(extra) }}</span></li>{% endfor %}</ul>{% endif %}
|
|
36
|
-
{
|
|
37
|
-
<ul>{% for fact in row.unresolvedFeasibilityFacts %}<li>{{ fact.fact | inline_code(extra) }} — {{ t('tasks.implementation-option-selection.why-it-matters') }} {{ fact.whyItMatters | inline_code(extra) }} <span class="evidence-refs">{{ t('macros.layout.evidence') }} {{ fact.evidence | evidence_refs(extra) }}</span></li>{% endfor %}</ul>{% endif %}
|
|
36
|
+
{{ unresolved_facts(row, extra) }}
|
|
38
37
|
</div>
|
|
39
38
|
<div class="option-detail">
|
|
40
39
|
<h4>{{ t('tasks.implementation-option-selection.expected-outcome') }}</h4>
|
|
@@ -48,6 +47,11 @@
|
|
|
48
47
|
</article>
|
|
49
48
|
{%- endmacro %}
|
|
50
49
|
|
|
50
|
+
{% macro unresolved_facts(row, extra) -%}
|
|
51
|
+
{% if row.unresolvedFeasibilityFacts %}<p><strong>{{ t('tasks.implementation-option-selection.unresolved-facts') }}</strong></p>
|
|
52
|
+
<ul>{% for fact in row.unresolvedFeasibilityFacts %}<li>{% if fact.get('resolutionKind') == 'technical-verification' %}{{ t('tasks.implementation-option-selection.technical-verification') }}: {% elif fact.get('resolutionKind') == 'user-decision' %}{{ t('tasks.implementation-option-selection.user-decision') }} ({{ fact.get('clarificationRefs', []) | join(', ') | inline_code(extra) }}): {% else %}{{ t('tasks.implementation-option-selection.classification-required') }}: {% endif %}{{ fact.fact | inline_code(extra) }} — {{ t('tasks.implementation-option-selection.why-it-matters') }} {{ fact.whyItMatters | inline_code(extra) }} <span class="evidence-refs">{{ t('macros.layout.evidence') }} {{ fact.evidence | evidence_refs(extra) }}</span></li>{% endfor %}</ul>{% endif %}
|
|
53
|
+
{%- endmacro %}
|
|
54
|
+
|
|
51
55
|
{% block human_content %}
|
|
52
56
|
<section data-report-section="selection-status">
|
|
53
57
|
<h2>{{ t('tasks.implementation-option-selection.selection-status') }}</h2>
|
|
@@ -89,6 +93,6 @@
|
|
|
89
93
|
|
|
90
94
|
<section data-report-section="candidate-audit" data-report-field="implementationOptionSelection.candidateAudit">
|
|
91
95
|
<h2>{{ t('tasks.implementation-option-selection.candidate-audit') }}</h2>
|
|
92
|
-
<table><thead><tr><th>{{ t('tasks.implementation-option-selection.candidate') }}</th><th>{{ t('tasks.implementation-option-selection.goal-column') }}</th><th>{{ t('tasks.implementation-option-selection.disposition') }}</th><th>{{ t('tasks.implementation-option-selection.reason') }}</th><th>{{ t('macros.layout.coverage-percent') }}</th><th>{{ t('macros.layout.scope-precision-percent') }}</th></tr></thead><tbody>{% for row in selection.candidateAudit %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[(t('macros.layout.id'), row.id), (t('macros.layout.proposed-by'), row.proposedBy)]) }}<td>{{ row.goal | inline_code(cardLinks.get(row.id, {})) }}{% if row.get("scopeCommitments") %}<ul class="audit-commitments">{% for item in row.scopeCommitments %}<li id="id-{{ row.id }}-{{ item.id }}">{{ item.id | inline_code(cardLinks.get(row.id, {})) }} · {{ item.commitment | inline_code(cardLinks.get(row.id, {})) }}</li>{% endfor %}</ul>{% endif %}</td><td>{{ row.disposition }}{% if row.mergedInto %} → {{ row.mergedInto | inline_code }}{% endif %}</td><td>{{ row.reason | inline_code }}</td><td>{{ row.coverageSummary.coveragePercent }}</td><td>{{ row.coverageSummary.scopePrecisionPercent }}</td></tr>{% else %}<tr><td colspan="6">{{ t('tasks.implementation-option-selection.no-audit-entries') }}</td></tr>{% endfor %}</tbody></table>
|
|
96
|
+
<table><thead><tr><th>{{ t('tasks.implementation-option-selection.candidate') }}</th><th>{{ t('tasks.implementation-option-selection.goal-column') }}</th><th>{{ t('tasks.implementation-option-selection.disposition') }}</th><th>{{ t('tasks.implementation-option-selection.reason') }}</th><th>{{ t('macros.layout.coverage-percent') }}</th><th>{{ t('macros.layout.scope-precision-percent') }}</th></tr></thead><tbody>{% for row in selection.candidateAudit %}<tr id="id-{{ row.id }}">{{ row_key(pairs=[(t('macros.layout.id'), row.id), (t('macros.layout.proposed-by'), row.proposedBy)]) }}<td>{{ row.goal | inline_code(cardLinks.get(row.id, {})) }}{% if row.get("scopeCommitments") %}<ul class="audit-commitments">{% for item in row.scopeCommitments %}<li id="id-{{ row.id }}-{{ item.id }}">{{ item.id | inline_code(cardLinks.get(row.id, {})) }} · {{ item.commitment | inline_code(cardLinks.get(row.id, {})) }}</li>{% endfor %}</ul>{% endif %}</td><td>{{ row.disposition }}{% if row.mergedInto %} → {{ row.mergedInto | inline_code }}{% endif %}</td><td>{{ row.reason | inline_code }}{{ unresolved_facts(row, cardLinks.get(row.id, {})) }}</td><td>{{ row.coverageSummary.coveragePercent }}</td><td>{{ row.coverageSummary.scopePrecisionPercent }}</td></tr>{% else %}<tr><td colspan="6">{{ t('tasks.implementation-option-selection.no-audit-entries') }}</td></tr>{% endfor %}</tbody></table>
|
|
93
97
|
</section>
|
|
94
98
|
{% endblock %}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{% extends "html/base.template.html" %}
|
|
2
|
+
{% block human_content %}
|
|
3
|
+
<section data-report-section="technical-source" data-report-field="technicalVerification.sourceReport">
|
|
4
|
+
<h2>{{ t('tasks.technical-verification.source') }}</h2>
|
|
5
|
+
<p>{{ verification.sourceReport | inline_code }}</p>
|
|
6
|
+
<p>{{ t('tasks.technical-verification.scope') }}</p>
|
|
7
|
+
</section>
|
|
8
|
+
<section data-report-section="technical-checks" data-report-field="technicalVerification.checks">
|
|
9
|
+
<h2>{{ t('tasks.technical-verification.checks') }}</h2>
|
|
10
|
+
{% for row in verification.checks %}
|
|
11
|
+
<article id="id-{{ row.id }}">
|
|
12
|
+
<h3>{{ row.id }} · {{ row.candidateId }} · {{ row.status }}</h3>
|
|
13
|
+
<p>{{ row.fact | inline_code }}</p>
|
|
14
|
+
<p>{{ row.hypothesis | inline_code }}</p>
|
|
15
|
+
<p>{{ row.procedure | inline_code }}</p>
|
|
16
|
+
<dl>
|
|
17
|
+
<dt>{{ t('tasks.technical-verification.confirming') }}</dt><dd>{{ row.confirmingSignal | inline_code }}</dd>
|
|
18
|
+
<dt>{{ t('tasks.technical-verification.rejecting') }}</dt><dd>{{ row.rejectingSignal | inline_code }}</dd>
|
|
19
|
+
</dl>
|
|
20
|
+
<p>{{ row.observation | inline_code }}</p>
|
|
21
|
+
{% for command in row.commands %}
|
|
22
|
+
<pre><code>{{ command.command }}</code></pre>
|
|
23
|
+
<p><code>{{ command.cwd }}</code> · {{ command.exitCode }}</p>
|
|
24
|
+
<p>{{ [command.logPath] | code_evidence }}</p>
|
|
25
|
+
{% endfor %}
|
|
26
|
+
<ul>{% for limitation in row.limitations %}<li>{{ limitation | inline_code }}</li>{% endfor %}</ul>
|
|
27
|
+
</article>
|
|
28
|
+
{% endfor %}
|
|
29
|
+
</section>
|
|
30
|
+
<section data-report-section="technical-routing" data-report-field="technicalVerification.routing">
|
|
31
|
+
<h2>{{ t('tasks.technical-verification.next') }}</h2>
|
|
32
|
+
<p>{{ verification.routing.nextTaskType | inline_code }}</p>
|
|
33
|
+
<p>{{ verification.routing.rationale | inline_code }}</p>
|
|
34
|
+
</section>
|
|
35
|
+
{% endblock %}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{% import "md/macros/sections.md" as s with context -%}
|
|
2
|
+
{{ s.section("technicalVerification.sourceReport", "Source Comparison") }}
|
|
3
|
+
{{ s.section("technicalVerification.checks", "Experiment Plans and Observations") }}
|
|
4
|
+
{{ s.section("technicalVerification.routing", "Return to Comparison") }}
|
|
5
|
+
{{ md_rest("technicalVerification", 3) }}
|
|
@@ -87,6 +87,7 @@ from okstra_ctl.stage_citations import enumerated_stage_numbers # noqa: E402
|
|
|
87
87
|
from okstra_ctl.plan_items import ( # noqa: E402
|
|
88
88
|
CRITIC_WORKER_ID,
|
|
89
89
|
advisory_plan_body_gating,
|
|
90
|
+
requires_plan_repair,
|
|
90
91
|
analyser_key as _analyser_key,
|
|
91
92
|
is_critic_worker,
|
|
92
93
|
lead_decision_basis,
|
|
@@ -116,6 +117,7 @@ from okstra_ctl.final_report_paths import ( # noqa: E402
|
|
|
116
117
|
translation_sidecar_path,
|
|
117
118
|
)
|
|
118
119
|
from okstra_token_usage.report import _match_worker_index # noqa: E402
|
|
120
|
+
from okstra_ctl.technical_verification import validate_technical_verification_report
|
|
119
121
|
from okstra_ctl.implementation_options import ( # noqa: E402
|
|
120
122
|
validate_blocked_answer_channel,
|
|
121
123
|
validate_implementation_option_selection,
|
|
@@ -3310,6 +3312,7 @@ def validate_final_report_data(
|
|
|
3310
3312
|
_validate_verifier_discrepancy_names_checklist_phase(
|
|
3311
3313
|
data, report_path, project_root, failures
|
|
3312
3314
|
)
|
|
3315
|
+
failures.extend(validate_technical_verification_report(data, report_path, project_root or report_path.parent))
|
|
3313
3316
|
if task_type == "implementation-option-selection":
|
|
3314
3317
|
selection = data.get("implementationOptionSelection") or {}
|
|
3315
3318
|
validation_root = project_root or report_path.parent
|
|
@@ -4163,7 +4166,7 @@ def _recompute_plan_body_gate(
|
|
|
4163
4166
|
return None
|
|
4164
4167
|
if all(c == "all-non-result" for c in classes):
|
|
4165
4168
|
return "aborted-non-result"
|
|
4166
|
-
if pbv.get("gating") is False:
|
|
4169
|
+
if pbv.get("gating") is False and not requires_plan_repair(pbv):
|
|
4167
4170
|
if any(c in ("majority-disagree", "has-dissent", "needs-reverify", "all-non-result") for c in classes):
|
|
4168
4171
|
return "passed-with-dissent"
|
|
4169
4172
|
return "passed"
|
|
@@ -4297,7 +4300,7 @@ def _gate_blocking_causes(
|
|
|
4297
4300
|
"""Which inputs actually block approval, as `gateBlockedBy` enum values."""
|
|
4298
4301
|
causes = set()
|
|
4299
4302
|
recomputed = _recompute_plan_body_gate(pbv, accepted_item_ids)
|
|
4300
|
-
if pbv.get("gating") is False:
|
|
4303
|
+
if pbv.get("gating") is False and not requires_plan_repair(pbv):
|
|
4301
4304
|
if recomputed == "aborted-non-result":
|
|
4302
4305
|
causes.add("non-result")
|
|
4303
4306
|
return causes
|
|
@@ -6181,6 +6184,8 @@ def _validate_advisory_plan_body_gating(data: dict, failures: list[str]) -> None
|
|
|
6181
6184
|
if not isinstance(pbv, dict) or pbv.get("gating") is not False:
|
|
6182
6185
|
return
|
|
6183
6186
|
facts = ip.get("designPreparation") is not None or ip.get("stageMap") or ip.get("stages")
|
|
6187
|
+
if requires_plan_repair(pbv):
|
|
6188
|
+
failures.append("plan-body-verification: objective verification defects require gating=true; repair the affected items")
|
|
6184
6189
|
if facts and not advisory_plan_body_gating(ip):
|
|
6185
6190
|
failures.append(
|
|
6186
6191
|
"final-report data.json: implementationPlanning.planBodyVerification "
|
|
@@ -7109,7 +7114,7 @@ def _validate_plan_body_clarification_matching(
|
|
|
7109
7114
|
# 게이트 계산은 이미 그것을 존중한다(`_recompute_plan_body_gate`,
|
|
7110
7115
|
# `_gate_blocking_causes`). 이 검사만 그 상태를 안 보면 자문 라운드가
|
|
7111
7116
|
# 승인 차단 행을 강제하게 되어, 막지 않기로 한 판정이 다시 막는다.
|
|
7112
|
-
if pbv.get("gating") is False:
|
|
7117
|
+
if pbv.get("gating") is False and not requires_plan_repair(pbv):
|
|
7113
7118
|
return
|
|
7114
7119
|
accepted = (
|
|
7115
7120
|
_resolved_noncritical_dissent_ids(data)
|
|
@@ -7178,7 +7183,7 @@ def _validate_self_fix_before_clarification(data: dict, failures: list[str]) ->
|
|
|
7178
7183
|
# 자문 라운드에 planner-fixable 과반 반대가 하나라도 나오면 통과 가능한
|
|
7179
7184
|
# 값이 없다. `okstra plan-items complete-round` 도 자문 라운드의 self-fix
|
|
7180
7185
|
# 기록을 거부하므로(plan_items_cli.py) 우회로도 없다.
|
|
7181
|
-
if pbv.get("gating") is False:
|
|
7186
|
+
if pbv.get("gating") is False and not requires_plan_repair(pbv):
|
|
7182
7187
|
return
|
|
7183
7188
|
if _self_fix_budget_exhausted(pbv):
|
|
7184
7189
|
return
|