create-principles-disciple 1.145.26 → 1.145.28
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/_release/product-identity.json +1 -1
- package/console/dist/server/models/RuleCandidateBindingConsoleModel.d.ts +26 -0
- package/console/dist/server/models/RuleCandidateBindingConsoleModel.js +309 -19
- package/console/dist/server/routes/rule-candidate-bindings.js +26 -1
- package/console/dist/shared/rule-candidate-binding-contract.d.ts +93 -8
- package/console/dist/shared/rule-candidate-binding-contract.js +4 -0
- package/console/dist/ui/api.d.ts +28 -0
- package/console/dist/ui/api.js +13 -1
- package/console/dist/ui/i18n/en.json +92 -20
- package/console/dist/ui/i18n/zh-CN.json +78 -6
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +29 -0
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +229 -3
- package/console/dist/ui/utils/rule-binding-validators.d.ts +5 -0
- package/console/dist/ui/utils/rule-binding-validators.js +149 -7
- package/console/dist/web/assets/app.css +6 -0
- package/console/dist/web/assets/app.js +32 -32
- package/core/dist/runtime-v2/evidence-triage/triage-policy.d.ts +0 -1
- package/core/dist/runtime-v2/evidence-triage/triage-policy.d.ts.map +1 -1
- package/core/dist/runtime-v2/evidence-triage/triage-policy.js +0 -1
- package/core/dist/runtime-v2/evidence-triage/triage-policy.js.map +1 -1
- package/core/dist/runtime-v2/golden-trace.d.ts.map +1 -1
- package/core/dist/runtime-v2/golden-trace.js +8 -1
- package/core/dist/runtime-v2/golden-trace.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +3 -3
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +3 -3
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-intent-preflight.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js +26 -95
- package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js.map +1 -1
- package/core/dist/runtime-v2/internalization/bash-risk.d.ts +32 -0
- package/core/dist/runtime-v2/internalization/bash-risk.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/bash-risk.js +785 -0
- package/core/dist/runtime-v2/internalization/bash-risk.js.map +1 -0
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +25 -2
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +177 -14
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +14 -2
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.d.ts +47 -0
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js +164 -3
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +9 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +39 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.js +59 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rule-source-correction.d.ts +60 -0
- package/core/dist/runtime-v2/internalization/rule-source-correction.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/rule-source-correction.js +214 -0
- package/core/dist/runtime-v2/internalization/rule-source-correction.js.map +1 -0
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js +65 -0
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts +14 -2
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-output.js +57 -2
- package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts +13 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js +15 -3
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-runner.d.ts +34 -0
- package/core/dist/runtime-v2/internalization/scribe-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-runner.js +128 -4
- package/core/dist/runtime-v2/internalization/scribe-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.d.ts +72 -0
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.js +100 -0
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.js.map +1 -0
- package/core/dist/runtime-v2/store/lifecycle/lease-manager.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/lifecycle/lease-manager.js +11 -2
- package/core/dist/runtime-v2/store/lifecycle/lease-manager.js.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js +26 -0
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js.map +1 -1
- package/core/dist/telemetry-event.d.ts +2 -2
- package/core/dist/telemetry-event.d.ts.map +1 -1
- package/core/dist/telemetry-event.js +5 -0
- package/core/dist/telemetry-event.js.map +1 -1
- package/core/package.json +5 -1
- package/host-runtime/dist/index.d.ts +0 -1
- package/host-runtime/dist/index.js +0 -3
- package/host-runtime/dist/production-rulehost-gate.js +11 -1
- package/host-runtime/package.json +1 -1
- package/package.json +2 -2
- package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js +32 -0
- package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +503 -490
- package/plugin/dist/hooks/pain-ingress-adapter.d.ts +1 -2
- package/plugin/dist/hooks/triage-adapter.d.ts +3 -1
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/package.json +2 -2
- package/core/dist/runtime-v2/pain-gate/index.d.ts +0 -10
- package/core/dist/runtime-v2/pain-gate/index.d.ts.map +0 -1
- package/core/dist/runtime-v2/pain-gate/index.js +0 -9
- package/core/dist/runtime-v2/pain-gate/index.js.map +0 -1
- package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.d.ts +0 -123
- package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.d.ts.map +0 -1
- package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.js +0 -159
- package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.js.map +0 -1
- package/host-runtime/dist/pain-evidence-ingress.d.ts +0 -16
- package/host-runtime/dist/pain-evidence-ingress.js +0 -15
- package/plugin/dist/core/pain-diagnostic-gate.d.ts +0 -47
- package/plugin/dist/core/thinking-os-parser.d.ts +0 -33
- package/plugin/dist/core/workflow-funnel-loader.d.ts +0 -72
|
@@ -49,29 +49,101 @@
|
|
|
49
49
|
"pages": {
|
|
50
50
|
"focus": {
|
|
51
51
|
"ruleBinding": {
|
|
52
|
-
"title": "Formal principle for
|
|
53
|
-
"separate": "
|
|
54
|
-
"unavailable": "
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
52
|
+
"title": "Formal principle attribution for rules",
|
|
53
|
+
"separate": "Attribution only states which principle a rule belongs to; it does not approve the rule, enable it, or replace a development design approval.",
|
|
54
|
+
"unavailable": "Attribution material is unavailable. Reload the page.",
|
|
55
|
+
"countsLine": "{{candidates}} rule candidates | {{painGroups}} feedback signals | {{bound}} linked | {{unlinked}} awaiting decision | {{blocked}} blocked",
|
|
56
|
+
"truncatedNote": "Too many candidates; showing the first {{count}}. The counts above cover displayed entries only.",
|
|
57
|
+
"groupPrefix": "From feedback:",
|
|
58
|
+
"painUnrecorded": "Original words not recorded",
|
|
59
|
+
"sourceUnreadable": "Records unreadable",
|
|
60
|
+
"sourceUnknown": "Source not recorded",
|
|
61
|
+
"sourceManual": "Owner feedback",
|
|
62
|
+
"sourceToolFailure": "Tool failure",
|
|
63
|
+
"groupCount": "{{count}} rule candidates",
|
|
64
|
+
"currentStage": "Current stage: ",
|
|
65
|
+
"boundTo": "Linked principle:",
|
|
66
|
+
"viewPrinciple": "Open principle",
|
|
67
|
+
"blockedLine": "Generation stopped at \"{{stage}}\": {{reason}}; no new rule generated yet.",
|
|
68
|
+
"blockedNoOwnerApproval": "No Owner approval needed right now - this is a material problem; the draft must be revised first.",
|
|
69
|
+
"blockedDetail": "Show blocking detail",
|
|
70
|
+
"blockReason": {
|
|
71
|
+
"textual_basis_unanchored": "draft citation check failed (the claimed Owner quote cannot be anchored in the material)"
|
|
72
|
+
},
|
|
73
|
+
"blockReasonUnknown": "generation blocked (server gave no reason code)",
|
|
74
|
+
"attentionLine": "Material problem ({{reason}}): shown as the server reports it; do not infer the attribution state from this.",
|
|
75
|
+
"nextLabel": "Next step: ",
|
|
76
|
+
"nextAwaitingOwner": "Choose which formal principle this rule implements - attribution only; it approves no code and enables no interception.",
|
|
77
|
+
"nextBoundGenerating": "The system is revising and validating the new rule artifact; no Owner action needed until it finishes.",
|
|
78
|
+
"nextBoundBlocked": "The draft must be revised and regenerated; no retry button is offered until the official recovery capability exists.",
|
|
79
|
+
"nextBoundBlockedNoResume": "Generation is blocked and cannot be resumed now ({{reason}}); revise the draft. An official recovery entry arrives in a later release.",
|
|
80
|
+
"nextBoundAdvanced": "Rule artifact generated; rule approval and activation happen on their own pages.",
|
|
81
|
+
"nextBoundNotBuilt": "Linked to a principle; rule generation has not started yet.",
|
|
82
|
+
"nextBoundAmbiguous": "Generation chain state is unclear; check technical details or reload.",
|
|
83
|
+
"nextRevoked": "Attribution withdrawn and artifacts kept; you may link again.",
|
|
84
|
+
"nextAttention": "Check the reason code in technical details, or reload.",
|
|
85
|
+
"techDetails": "Technical details",
|
|
86
|
+
"candidate": "Rule candidate text",
|
|
87
|
+
"pain": "Source pain signal",
|
|
59
88
|
"evidence": "Diagnosis evidence",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
89
|
+
"boundSince": "link revision {{revision}} ({{date}})",
|
|
90
|
+
"chooseTarget": "Choose the principle this rule implements: attribution only - it approves no code, enables no interception, and does not mean a development SPEC was approved.",
|
|
62
91
|
"search": "Search formal principles",
|
|
63
|
-
"noTarget": "
|
|
64
|
-
"formation": "
|
|
92
|
+
"noTarget": "If no principle fits, keep it unlinked.",
|
|
93
|
+
"formation": "Open the principle formation flow",
|
|
65
94
|
"version": "Version",
|
|
66
|
-
"declaration": "Submitting
|
|
67
|
-
"reason": "Reason for
|
|
68
|
-
"bind": "
|
|
69
|
-
"
|
|
70
|
-
"bound": "
|
|
71
|
-
"resumeFailed": "
|
|
72
|
-
"revoked": "
|
|
73
|
-
"revokeBoundary": "
|
|
74
|
-
"stop": "
|
|
95
|
+
"declaration": "Submitting means: this rule is one implementation of the selected principle, and you authorize exactly one controlled revision/generation/validation run under the existing configuration.",
|
|
96
|
+
"reason": "Reason for linking or withdrawing",
|
|
97
|
+
"bind": "Link and run one revision",
|
|
98
|
+
"revokeSecondary": "Withdraw attribution (secondary)",
|
|
99
|
+
"bound": "Attribution saved; one controlled revision was requested. The rule still requires separate approval.",
|
|
100
|
+
"resumeFailed": "Attribution saved but the revision has not started. Check the recovery reason; old artifacts are unchanged and will not auto-retry.",
|
|
101
|
+
"revoked": "Attribution withdrawn; artifacts kept. Running rules must be stopped separately.",
|
|
102
|
+
"revokeBoundary": "Withdrawal does not stop running rules; in-flight operations that passed final checks may still complete.",
|
|
103
|
+
"stop": "Open rule runs and stop entry",
|
|
104
|
+
"openReview": "Review and act",
|
|
105
|
+
"closeReview": "Collapse",
|
|
106
|
+
"reviseCta": "Revise and continue",
|
|
107
|
+
"reviseStarted": "One controlled revision was requested; generation and validation continue under the existing configuration.",
|
|
108
|
+
"reviseAlreadyStarted": "This revision is already in progress; no repeat request needed.",
|
|
109
|
+
"resumeUnavailable": "Automatic revision is unavailable now ({{reason}}); a generic retry is not a source revision.",
|
|
110
|
+
"sourceCorrectionCta": "Correct source material and continue",
|
|
111
|
+
"sourceCorrectionStarted": "One source-material correction was authorized: the Scribe reopens for one round on the corrected material and the original Artificer resumes on the new artifact, under the existing configuration and budgets.",
|
|
112
|
+
"sourceCorrectionAlreadyStarted": "This source correction is already in progress (idempotent replay); no repeat request needed.",
|
|
113
|
+
"sourceCorrectionUnavailable": "The source-correction entry is unavailable now ({{reason}}).",
|
|
114
|
+
"resumeReason": {
|
|
115
|
+
"revision_budget_exhausted": "the revision budget of this link is spent",
|
|
116
|
+
"dreamer_lineage_not_unique": "dreamer task lineage is not unique",
|
|
117
|
+
"dreamer_missing": "dreamer task not found",
|
|
118
|
+
"philosopher_lineage_not_unique": "philosopher task lineage is not unique",
|
|
119
|
+
"philosopher_missing": "philosopher task not found",
|
|
120
|
+
"scribe_lineage_not_unique": "scribe task lineage is not unique",
|
|
121
|
+
"scribe_metadata_invalid": "scribe task metadata is invalid",
|
|
122
|
+
"predecessor_not_succeeded": "an upstream task has not succeeded",
|
|
123
|
+
"scribe_missing": "scribe task not found",
|
|
124
|
+
"candidate_kind_invalid": "candidate kind is invalid",
|
|
125
|
+
"binding_revision_stale": "binding revision is stale; reload",
|
|
126
|
+
"task_revision_stale": "task revision epoch is stale; reload",
|
|
127
|
+
"scribe_target_mismatch": "target scribe task mismatch; reload",
|
|
128
|
+
"task_lease_present": "the task is held by a worker; retry later",
|
|
129
|
+
"revision_snapshot_stale": "task state changed; reload"
|
|
130
|
+
},
|
|
131
|
+
"stateAwaitingOwner": "Awaiting principle choice",
|
|
132
|
+
"stateBoundGenerating": "Linked - generating",
|
|
133
|
+
"stateBoundBlocked": "Linked - blocked",
|
|
134
|
+
"stateBoundAdvanced": "Linked - generated",
|
|
135
|
+
"stateBoundNotBuilt": "Linked - not started",
|
|
136
|
+
"stateBoundAmbiguous": "Linked - chain unclear",
|
|
137
|
+
"stateRevoked": "Attribution withdrawn",
|
|
138
|
+
"stateAttention": "Material problem",
|
|
139
|
+
"stageDreamer": "dreaming",
|
|
140
|
+
"stagePhilosopher": "distilling",
|
|
141
|
+
"stageScribe": "drafting",
|
|
142
|
+
"stageArtificer": "rule generation",
|
|
143
|
+
"stageEvaluator": "rule validation",
|
|
144
|
+
"stageRolloutReviewer": "rollout review",
|
|
145
|
+
"viewRemaining": "Show the other {{count}} feedback groups",
|
|
146
|
+
"hideRemaining": "Collapse remaining feedback groups"
|
|
75
147
|
},
|
|
76
148
|
"eyebrow": "Governance Focus",
|
|
77
149
|
"title": "What deserves your judgment right now",
|
|
@@ -52,13 +52,42 @@
|
|
|
52
52
|
"title": "规则的正式原则归属",
|
|
53
53
|
"separate": "关联仅声明规则属于哪条原则;不批准规则、不启用规则,也不代替开发设计批准。",
|
|
54
54
|
"unavailable": "关联材料暂不可读,请重新加载。",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
55
|
+
"countsLine": "共 {{candidates}} 条规则候选 · 来自 {{painGroups}} 次反馈 · {{bound}} 已关联 · {{unlinked}} 待决定 · {{blocked}} 受阻",
|
|
56
|
+
"truncatedNote": "候选较多,已按上限显示前 {{count}} 条;上方计数仅覆盖已显示条目。",
|
|
57
|
+
"groupPrefix": "来自反馈:",
|
|
58
|
+
"painUnrecorded": "原话未记录",
|
|
59
|
+
"sourceUnreadable": "记录暂不可读",
|
|
60
|
+
"sourceUnknown": "来源未记录",
|
|
61
|
+
"sourceManual": "拥有者反馈",
|
|
62
|
+
"sourceToolFailure": "工具失败",
|
|
63
|
+
"groupCount": "{{count}} 条规则候选",
|
|
64
|
+
"currentStage": "当前阶段:",
|
|
65
|
+
"boundTo": "已关联原则:",
|
|
66
|
+
"viewPrinciple": "查看原则详情",
|
|
67
|
+
"blockedLine": "生成停在「{{stage}}」:{{reason}};尚未生成新规则。",
|
|
68
|
+
"blockedNoOwnerApproval": "当前无需拥有者批准——这是材料问题,需要先修订草案。",
|
|
69
|
+
"blockedDetail": "查看受阻原因",
|
|
70
|
+
"blockReason": {
|
|
71
|
+
"textual_basis_unanchored": "草案引用核验未通过(声明的拥有者原话在材料中找不到锚点)"
|
|
72
|
+
},
|
|
73
|
+
"blockReasonUnknown": "生成受阻(服务端未给出原因码)",
|
|
74
|
+
"attentionLine": "材料异常({{reason}}):按服务端事实展示,不能据此判断关联状态。",
|
|
75
|
+
"nextLabel": "下一步:",
|
|
76
|
+
"nextAwaitingOwner": "为这条规则选择要落实的正式原则——只决定归属,不批准代码、不启用拦截。",
|
|
77
|
+
"nextBoundGenerating": "系统正在修订与验证新规则工件;完成前无需拥有者操作。",
|
|
78
|
+
"nextBoundBlocked": "需要修订草案后重新生成;正式恢复能力就绪前页面不会提供重试按钮。",
|
|
79
|
+
"nextBoundBlockedNoResume": "生成受阻且当前不可恢复({{reason}});需修订草案,后续版本会提供正式恢复入口。",
|
|
80
|
+
"nextBoundAdvanced": "规则工件已生成;规则审批与生效在各自页面单独进行。",
|
|
81
|
+
"nextBoundNotBuilt": "已关联原则;尚未开始生成新规则。",
|
|
82
|
+
"nextBoundAmbiguous": "生成链状态不明;请查看技术详情或重新加载。",
|
|
83
|
+
"nextRevoked": "归属已撤回,历史工件保留;可重新关联。",
|
|
84
|
+
"nextAttention": "请查看技术详情中的原因码,或重新加载。",
|
|
85
|
+
"techDetails": "技术详情",
|
|
57
86
|
"candidate": "规则候选原文",
|
|
58
87
|
"pain": "来源痛苦信号",
|
|
59
88
|
"evidence": "诊断依据",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
89
|
+
"boundSince": "第 {{revision}} 次关联({{date}})",
|
|
90
|
+
"chooseTarget": "选择这条规则要落实的原则:只决定归属,不批准代码、不启用拦截,也不代表开发 SPEC 已获批准。",
|
|
62
91
|
"search": "搜索正式原则",
|
|
63
92
|
"noTarget": "没有合适的正式原则时,保持未关联。",
|
|
64
93
|
"formation": "查看既有原则形成流程",
|
|
@@ -66,12 +95,55 @@
|
|
|
66
95
|
"declaration": "提交表示:该规则是所选原则在上述范围内的一种实现,并授权按现有配置自动续跑一次修订、生成和验证。",
|
|
67
96
|
"reason": "关联或撤回的理由",
|
|
68
97
|
"bind": "关联并自动续跑一次",
|
|
69
|
-
"
|
|
98
|
+
"revokeSecondary": "撤回归属(次级)",
|
|
70
99
|
"bound": "关联已保存,已请求一次受控修订;后续规则仍须单独审批。",
|
|
71
100
|
"resumeFailed": "关联已保存,但修订尚未开始。请查看恢复原因;旧工件未变,不会自动重试。",
|
|
72
101
|
"revoked": "归属已撤回;历史工件保留。运行中的规则须另行停用。",
|
|
73
102
|
"revokeBoundary": "撤回不停止已有运行规则;撤回前已通过最终检查的在途操作仍可能完成。",
|
|
74
|
-
"stop": "查看规则运行与停用入口"
|
|
103
|
+
"stop": "查看规则运行与停用入口",
|
|
104
|
+
"openReview": "查看并处理",
|
|
105
|
+
"closeReview": "收起",
|
|
106
|
+
"reviseCta": "修订并继续",
|
|
107
|
+
"reviseStarted": "已请求一次受控修订;生成与验证将按现有配置继续。",
|
|
108
|
+
"reviseAlreadyStarted": "该修订已在进行中;无需重复请求。",
|
|
109
|
+
"resumeUnavailable": "当前不可自动修订({{reason}});不要把通用重试当作源修订。",
|
|
110
|
+
"sourceCorrectionCta": "修正源材料并继续",
|
|
111
|
+
"sourceCorrectionStarted": "已授权一次源材料修订:Scribe 将以修正后的材料重开一轮,原 Artificer 在新工件上定点续接;沿用原配置与预算。",
|
|
112
|
+
"sourceCorrectionAlreadyStarted": "该源材料修订已在进行中(幂等重放);无需重复请求。",
|
|
113
|
+
"sourceCorrectionUnavailable": "源材料修订入口当前不可用({{reason}})。",
|
|
114
|
+
"resumeReason": {
|
|
115
|
+
"revision_budget_exhausted": "本次关联的修订预算已用完",
|
|
116
|
+
"dreamer_lineage_not_unique": "构思任务血缘不唯一",
|
|
117
|
+
"dreamer_missing": "找不到构思任务",
|
|
118
|
+
"philosopher_lineage_not_unique": "提炼任务血缘不唯一",
|
|
119
|
+
"philosopher_missing": "找不到提炼任务",
|
|
120
|
+
"scribe_lineage_not_unique": "草稿任务血缘不唯一",
|
|
121
|
+
"scribe_metadata_invalid": "草稿任务元数据无效",
|
|
122
|
+
"predecessor_not_succeeded": "上游任务未成功完成",
|
|
123
|
+
"scribe_missing": "找不到草稿任务",
|
|
124
|
+
"candidate_kind_invalid": "候选类型无效",
|
|
125
|
+
"binding_revision_stale": "关联版本已过期,请重新加载",
|
|
126
|
+
"task_revision_stale": "任务修订版本已过期,请重新加载",
|
|
127
|
+
"scribe_target_mismatch": "目标草稿任务不匹配,请重新加载",
|
|
128
|
+
"task_lease_present": "任务正被执行方持有,稍后重试",
|
|
129
|
+
"revision_snapshot_stale": "任务状态已变化,请重新加载"
|
|
130
|
+
},
|
|
131
|
+
"stateAwaitingOwner": "待选择原则",
|
|
132
|
+
"stateBoundGenerating": "已关联 · 生成中",
|
|
133
|
+
"stateBoundBlocked": "已关联 · 受阻",
|
|
134
|
+
"stateBoundAdvanced": "已关联 · 已生成",
|
|
135
|
+
"stateBoundNotBuilt": "已关联 · 未开始生成",
|
|
136
|
+
"stateBoundAmbiguous": "已关联 · 链状态不明",
|
|
137
|
+
"stateRevoked": "已撤回归属",
|
|
138
|
+
"stateAttention": "材料异常",
|
|
139
|
+
"stageDreamer": "构思",
|
|
140
|
+
"stagePhilosopher": "提炼",
|
|
141
|
+
"stageScribe": "草稿撰写",
|
|
142
|
+
"stageArtificer": "规则生成",
|
|
143
|
+
"stageEvaluator": "规则验证",
|
|
144
|
+
"stageRolloutReviewer": "发布复核",
|
|
145
|
+
"viewRemaining": "显示其余 {{count}} 组反馈",
|
|
146
|
+
"hideRemaining": "收起其余反馈组"
|
|
75
147
|
},
|
|
76
148
|
"eyebrow": "治理焦点",
|
|
77
149
|
"title": "现在,值得你判断的事",
|
|
@@ -1,2 +1,31 @@
|
|
|
1
|
+
import type { RuleBindingQueueItem } from '../../../shared/rule-candidate-binding-contract.js';
|
|
2
|
+
export type RuleBindingCardState = 'awaiting_owner' | 'bound_generating' | 'bound_blocked' | 'bound_advanced' | 'bound_not_built' | 'bound_ambiguous' | 'revoked' | 'attention';
|
|
3
|
+
/** ERR-106: exhaustive display map over the card state union — no default branch. */
|
|
4
|
+
export declare const RULE_BINDING_CARD_STATE_KEY: Record<RuleBindingCardState, string>;
|
|
5
|
+
export declare const RULE_BINDING_CARD_STATE_CLASS: Record<RuleBindingCardState, string>;
|
|
6
|
+
export declare function deriveRuleBindingCardState(item: RuleBindingQueueItem): RuleBindingCardState;
|
|
7
|
+
export interface RuleBindingPainGroup {
|
|
8
|
+
key: string;
|
|
9
|
+
pain: RuleBindingQueueItem['pain'];
|
|
10
|
+
items: RuleBindingQueueItem[];
|
|
11
|
+
}
|
|
12
|
+
/** Group by canonical pain id; a missing pain id gets its own per-candidate
|
|
13
|
+
* group so unattributable candidates never vanish into someone else's group. */
|
|
14
|
+
export declare function groupRuleBindingQueue(items: RuleBindingQueueItem[]): RuleBindingPainGroup[];
|
|
15
|
+
/** The resume action may only appear when the real backend says ready AND the
|
|
16
|
+
* chain is not currently running — a revision during an active run would be
|
|
17
|
+
* a fake retry, not the bounded source revision. */
|
|
18
|
+
export declare function canOfferRuleBindingResume(item: RuleBindingQueueItem): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* PRI-1048 R6 #4: the source-correction entry applies when the bounded
|
|
21
|
+
* source-correction budget is available on a bound+blocked chain — the shape
|
|
22
|
+
* where the binding revision budget is typically already spent (the 52d
|
|
23
|
+
* production candidate). Real eligibility is resolved server-side
|
|
24
|
+
* (previewRuleSourceCorrection); click-time authority is reviseRuleSourceOnce.
|
|
25
|
+
*/
|
|
26
|
+
export declare function canOfferSourceCorrection(item: Pick<RuleBindingQueueItem, 'binding' | 'generation' | 'sourceCorrection'>): boolean;
|
|
27
|
+
/** Model-synthesized fallback sources must stay localized (CodeRabbit PRI-1048:
|
|
28
|
+
* `unknown` previously fell through to raw English in the zh-CN group header). */
|
|
29
|
+
export declare const PAIN_SOURCE_KEY: Record<string, string>;
|
|
1
30
|
/** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
|
|
2
31
|
export declare function RuleCandidateBindingPanel(): import("react").JSX.Element | null;
|
|
@@ -2,18 +2,155 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { Link } from 'react-router-dom';
|
|
5
|
-
import { fetchRuleBindingQueue, fetchRuleBindingReview, mutateRuleBinding } from '../../api.js';
|
|
5
|
+
import { fetchRuleBindingQueue, fetchRuleBindingReview, mutateRuleBinding, reviseRuleBindingOnce, correctRuleSource } from '../../api.js';
|
|
6
|
+
import { splitReadingTitleBody } from '../../utils/reading-layout.js';
|
|
7
|
+
/**
|
|
8
|
+
* PRI-1048 治理焦点「规则的正式原则归属」队列。
|
|
9
|
+
* 按痛苦信号分组的有界卡片列表;每张卡默认回答三问:
|
|
10
|
+
* 这是哪次反馈 / 现在卡在哪 / 我要做什么。技术 ID 与原始 JSON 只进「技术详情」。
|
|
11
|
+
* 归属、规则审批、激活三权分立不变(文案见 separate/declaration)。
|
|
12
|
+
*/
|
|
13
|
+
/** Collapsed first screen shows at most this many pain groups. */
|
|
14
|
+
const COLLAPSED_GROUP_LIMIT = 3;
|
|
15
|
+
/** ERR-106: exhaustive display map over the card state union — no default branch. */
|
|
16
|
+
export const RULE_BINDING_CARD_STATE_KEY = {
|
|
17
|
+
awaiting_owner: 'pages.focus.ruleBinding.stateAwaitingOwner',
|
|
18
|
+
bound_generating: 'pages.focus.ruleBinding.stateBoundGenerating',
|
|
19
|
+
bound_blocked: 'pages.focus.ruleBinding.stateBoundBlocked',
|
|
20
|
+
bound_advanced: 'pages.focus.ruleBinding.stateBoundAdvanced',
|
|
21
|
+
bound_not_built: 'pages.focus.ruleBinding.stateBoundNotBuilt',
|
|
22
|
+
bound_ambiguous: 'pages.focus.ruleBinding.stateBoundAmbiguous',
|
|
23
|
+
revoked: 'pages.focus.ruleBinding.stateRevoked',
|
|
24
|
+
attention: 'pages.focus.ruleBinding.stateAttention',
|
|
25
|
+
};
|
|
26
|
+
export const RULE_BINDING_CARD_STATE_CLASS = {
|
|
27
|
+
awaiting_owner: 'border-gov/40 text-gov',
|
|
28
|
+
bound_generating: 'border-green/40 text-green',
|
|
29
|
+
bound_blocked: 'border-amber/45 text-amber',
|
|
30
|
+
bound_advanced: 'border-green/40 text-green',
|
|
31
|
+
bound_not_built: 'border-line text-ink-3',
|
|
32
|
+
bound_ambiguous: 'border-amber/45 text-amber',
|
|
33
|
+
revoked: 'border-line text-ink-3',
|
|
34
|
+
attention: 'border-amber/45 text-amber',
|
|
35
|
+
};
|
|
36
|
+
export function deriveRuleBindingCardState(item) {
|
|
37
|
+
switch (item.binding.status) {
|
|
38
|
+
case 'unlinked': return 'awaiting_owner';
|
|
39
|
+
case 'binding_revoked': return 'revoked';
|
|
40
|
+
case 'attention': return 'attention';
|
|
41
|
+
case 'bound':
|
|
42
|
+
switch (item.generation?.stage) {
|
|
43
|
+
case null:
|
|
44
|
+
case undefined:
|
|
45
|
+
case 'not_built': return 'bound_not_built';
|
|
46
|
+
case 'in_progress': return 'bound_generating';
|
|
47
|
+
case 'blocked': return 'bound_blocked';
|
|
48
|
+
case 'advanced': return 'bound_advanced';
|
|
49
|
+
case 'ambiguous': return 'bound_ambiguous';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Group by canonical pain id; a missing pain id gets its own per-candidate
|
|
54
|
+
* group so unattributable candidates never vanish into someone else's group. */
|
|
55
|
+
export function groupRuleBindingQueue(items) {
|
|
56
|
+
const groups = new Map();
|
|
57
|
+
for (const item of items) {
|
|
58
|
+
const key = item.pain.painId === '' ? `candidate:${item.candidateId}` : item.pain.painId;
|
|
59
|
+
const group = groups.get(key) ?? { key, pain: item.pain, items: [] };
|
|
60
|
+
group.items.push(item);
|
|
61
|
+
groups.set(key, group);
|
|
62
|
+
}
|
|
63
|
+
return [...groups.values()];
|
|
64
|
+
}
|
|
65
|
+
/** The resume action may only appear when the real backend says ready AND the
|
|
66
|
+
* chain is not currently running — a revision during an active run would be
|
|
67
|
+
* a fake retry, not the bounded source revision. */
|
|
68
|
+
export function canOfferRuleBindingResume(item) {
|
|
69
|
+
return item.binding.status === 'bound'
|
|
70
|
+
&& item.resume?.status === 'ready'
|
|
71
|
+
&& item.generation?.stage !== 'in_progress'
|
|
72
|
+
&& item.generation?.stage !== 'ambiguous';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* PRI-1048 R6 #4: the source-correction entry applies when the bounded
|
|
76
|
+
* source-correction budget is available on a bound+blocked chain — the shape
|
|
77
|
+
* where the binding revision budget is typically already spent (the 52d
|
|
78
|
+
* production candidate). Real eligibility is resolved server-side
|
|
79
|
+
* (previewRuleSourceCorrection); click-time authority is reviseRuleSourceOnce.
|
|
80
|
+
*/
|
|
81
|
+
export function canOfferSourceCorrection(item) {
|
|
82
|
+
return item.binding.status === 'bound'
|
|
83
|
+
&& item.generation?.stage === 'blocked'
|
|
84
|
+
&& item.sourceCorrection?.status === 'ready';
|
|
85
|
+
}
|
|
86
|
+
/** ERR-106: next-step copy and the left bar also use exhaustive maps. */
|
|
87
|
+
const RULE_BINDING_NEXT_STEP_KEY = {
|
|
88
|
+
awaiting_owner: 'pages.focus.ruleBinding.nextAwaitingOwner',
|
|
89
|
+
bound_generating: 'pages.focus.ruleBinding.nextBoundGenerating',
|
|
90
|
+
bound_blocked: 'pages.focus.ruleBinding.nextBoundBlocked',
|
|
91
|
+
bound_advanced: 'pages.focus.ruleBinding.nextBoundAdvanced',
|
|
92
|
+
bound_not_built: 'pages.focus.ruleBinding.nextBoundNotBuilt',
|
|
93
|
+
bound_ambiguous: 'pages.focus.ruleBinding.nextBoundAmbiguous',
|
|
94
|
+
revoked: 'pages.focus.ruleBinding.nextRevoked',
|
|
95
|
+
attention: 'pages.focus.ruleBinding.nextAttention',
|
|
96
|
+
};
|
|
97
|
+
const RULE_BINDING_CARD_STATE_BAR = {
|
|
98
|
+
awaiting_owner: 'bg-gov',
|
|
99
|
+
bound_generating: 'bg-green',
|
|
100
|
+
bound_blocked: 'bg-amber',
|
|
101
|
+
bound_advanced: 'bg-green',
|
|
102
|
+
bound_not_built: 'bg-line-2',
|
|
103
|
+
bound_ambiguous: 'bg-amber',
|
|
104
|
+
revoked: 'bg-line-2',
|
|
105
|
+
attention: 'bg-amber',
|
|
106
|
+
};
|
|
107
|
+
/** Model-synthesized fallback sources must stay localized (CodeRabbit PRI-1048:
|
|
108
|
+
* `unknown` previously fell through to raw English in the zh-CN group header). */
|
|
109
|
+
export const PAIN_SOURCE_KEY = {
|
|
110
|
+
unreadable: 'pages.focus.ruleBinding.sourceUnreadable',
|
|
111
|
+
unknown: 'pages.focus.ruleBinding.sourceUnknown',
|
|
112
|
+
manual: 'pages.focus.ruleBinding.sourceManual',
|
|
113
|
+
tool_failure: 'pages.focus.ruleBinding.sourceToolFailure',
|
|
114
|
+
};
|
|
115
|
+
const STAGE_KIND_KEY = {
|
|
116
|
+
dreamer: 'pages.focus.ruleBinding.stageDreamer',
|
|
117
|
+
philosopher: 'pages.focus.ruleBinding.stagePhilosopher',
|
|
118
|
+
scribe: 'pages.focus.ruleBinding.stageScribe',
|
|
119
|
+
artificer: 'pages.focus.ruleBinding.stageArtificer',
|
|
120
|
+
evaluator: 'pages.focus.ruleBinding.stageEvaluator',
|
|
121
|
+
rollout_reviewer: 'pages.focus.ruleBinding.stageRolloutReviewer',
|
|
122
|
+
};
|
|
123
|
+
/** i18n with graceful fallback to the raw server code (visible, not dropped). */
|
|
124
|
+
function codeText(t, prefix, code) {
|
|
125
|
+
if (!code)
|
|
126
|
+
return '';
|
|
127
|
+
const key = `${prefix}.${code}`;
|
|
128
|
+
const text = t(key);
|
|
129
|
+
return text === key ? code : text;
|
|
130
|
+
}
|
|
131
|
+
function painSourceLabel(t, source) {
|
|
132
|
+
const key = PAIN_SOURCE_KEY[source];
|
|
133
|
+
return key ? t(key) : source;
|
|
134
|
+
}
|
|
135
|
+
function stageKindLabel(t, stageKind) {
|
|
136
|
+
if (!stageKind)
|
|
137
|
+
return null;
|
|
138
|
+
const key = STAGE_KIND_KEY[stageKind];
|
|
139
|
+
return key ? t(key) : stageKind;
|
|
140
|
+
}
|
|
6
141
|
/** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
|
|
7
142
|
export function RuleCandidateBindingPanel() {
|
|
8
143
|
const { t } = useTranslation();
|
|
9
144
|
const [queue, setQueue] = useState(null);
|
|
10
145
|
const [review, setReview] = useState(null);
|
|
146
|
+
const [openCandidateId, setOpenCandidateId] = useState('');
|
|
11
147
|
const [search, setSearch] = useState('');
|
|
12
148
|
const [selectedId, setSelectedId] = useState('');
|
|
13
149
|
const [reason, setReason] = useState('');
|
|
14
150
|
const [busy, setBusy] = useState(false);
|
|
15
151
|
const [message, setMessage] = useState('');
|
|
16
152
|
const [error, setError] = useState('');
|
|
153
|
+
const [showAllGroups, setShowAllGroups] = useState(false);
|
|
17
154
|
useEffect(() => {
|
|
18
155
|
let active = true;
|
|
19
156
|
void fetchRuleBindingQueue().then(result => {
|
|
@@ -32,12 +169,19 @@ export function RuleCandidateBindingPanel() {
|
|
|
32
169
|
const result = await fetchRuleBindingReview(candidateId, query);
|
|
33
170
|
if (result.success) {
|
|
34
171
|
setReview(result.data);
|
|
172
|
+
setOpenCandidateId(candidateId);
|
|
35
173
|
setSelectedId('');
|
|
36
174
|
}
|
|
37
175
|
else
|
|
38
176
|
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
39
177
|
setBusy(false);
|
|
40
178
|
}
|
|
179
|
+
function closeReview() { setOpenCandidateId(''); setReview(null); setSearch(''); setReason(''); }
|
|
180
|
+
async function refreshQueue() {
|
|
181
|
+
const result = await fetchRuleBindingQueue();
|
|
182
|
+
if (result.success)
|
|
183
|
+
setQueue(result.data);
|
|
184
|
+
}
|
|
41
185
|
async function decide(action) {
|
|
42
186
|
if (!review)
|
|
43
187
|
return;
|
|
@@ -56,13 +200,95 @@ export function RuleCandidateBindingPanel() {
|
|
|
56
200
|
setMessage(action === 'revoke' ? t('pages.focus.ruleBinding.revoked')
|
|
57
201
|
: result.data.revisionStatus === 'refused' ? t('pages.focus.ruleBinding.resumeFailed') : t('pages.focus.ruleBinding.bound'));
|
|
58
202
|
await load(review.candidateId, search);
|
|
203
|
+
await refreshQueue();
|
|
204
|
+
}
|
|
205
|
+
else
|
|
206
|
+
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
207
|
+
setBusy(false);
|
|
208
|
+
}
|
|
209
|
+
/** Only reachable when the real resolver said ready (canOfferRuleBindingResume). */
|
|
210
|
+
async function revise() {
|
|
211
|
+
if (!review?.resume || review.resume.status !== 'ready' || review.resume.scribeTaskId === null || review.resume.taskRevisionEpoch === null)
|
|
212
|
+
return;
|
|
213
|
+
setBusy(true);
|
|
214
|
+
setError('');
|
|
215
|
+
setMessage('');
|
|
216
|
+
const result = await reviseRuleBindingOnce(review.candidateId, {
|
|
217
|
+
expectedBindingRevision: review.currentRevision,
|
|
218
|
+
expectedScribeTaskId: review.resume.scribeTaskId,
|
|
219
|
+
expectedTaskRevisionEpoch: review.resume.taskRevisionEpoch,
|
|
220
|
+
requestId: crypto.randomUUID(),
|
|
221
|
+
});
|
|
222
|
+
if (result.success) {
|
|
223
|
+
setMessage(result.data.status === 'revision_already_started'
|
|
224
|
+
? t('pages.focus.ruleBinding.reviseAlreadyStarted')
|
|
225
|
+
: t('pages.focus.ruleBinding.reviseStarted'));
|
|
226
|
+
await load(review.candidateId, search);
|
|
227
|
+
await refreshQueue();
|
|
228
|
+
}
|
|
229
|
+
else
|
|
230
|
+
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
231
|
+
setBusy(false);
|
|
232
|
+
}
|
|
233
|
+
/** PRI-1048 R6 #4: only reachable when the server preview said ready
|
|
234
|
+
* (canOfferSourceCorrection); the server re-validates every CAS input. */
|
|
235
|
+
async function correctSource() {
|
|
236
|
+
const preview = review?.sourceCorrection;
|
|
237
|
+
if (!review || !preview || preview.status !== 'ready'
|
|
238
|
+
|| preview.scribeTaskId === null || preview.artificerTaskId === null
|
|
239
|
+
|| preview.scribeArtifactDigest === null || preview.expectedBindingRevision === null)
|
|
240
|
+
return;
|
|
241
|
+
setBusy(true);
|
|
242
|
+
setError('');
|
|
243
|
+
setMessage('');
|
|
244
|
+
const result = await correctRuleSource(review.candidateId, {
|
|
245
|
+
expectedBindingRevision: preview.expectedBindingRevision,
|
|
246
|
+
expectedScribeTaskId: preview.scribeTaskId,
|
|
247
|
+
expectedArtificerTaskId: preview.artificerTaskId,
|
|
248
|
+
expectedScribeArtifactDigest: preview.scribeArtifactDigest,
|
|
249
|
+
reason,
|
|
250
|
+
requestId: crypto.randomUUID(),
|
|
251
|
+
});
|
|
252
|
+
if (result.success) {
|
|
253
|
+
setMessage(result.data.status === 'revision_already_started'
|
|
254
|
+
? t('pages.focus.ruleBinding.sourceCorrectionAlreadyStarted')
|
|
255
|
+
: t('pages.focus.ruleBinding.sourceCorrectionStarted'));
|
|
256
|
+
await load(review.candidateId, search);
|
|
257
|
+
await refreshQueue();
|
|
59
258
|
}
|
|
60
259
|
else
|
|
61
260
|
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
62
261
|
setBusy(false);
|
|
63
262
|
}
|
|
64
|
-
if (queue
|
|
263
|
+
if ((queue === null || queue.items.length === 0) && !error)
|
|
65
264
|
return null;
|
|
265
|
+
const groups = groupRuleBindingQueue(queue?.items ?? []);
|
|
266
|
+
const visibleGroups = showAllGroups ? groups : groups.slice(0, COLLAPSED_GROUP_LIMIT);
|
|
66
267
|
const bound = review?.currentBinding?.action === 'bind';
|
|
67
|
-
return _jsxs("section", { className: "mb-7 rounded border border-line p-4", "data-testid": "rule-binding-panel", "aria-label": t('pages.focus.ruleBinding.title'), children: [_jsx("h2", { className: "text-sm font-medium", children: t('pages.focus.ruleBinding.title') }), _jsx("p", { className: "mt-2 text-sm text-ink-3", children: t('pages.focus.ruleBinding.separate') }),
|
|
268
|
+
return _jsxs("section", { className: "mb-7 rounded border border-line p-4", "data-testid": "rule-binding-panel", "aria-label": t('pages.focus.ruleBinding.title'), children: [_jsx("h2", { className: "text-sm font-medium", children: t('pages.focus.ruleBinding.title') }), _jsx("p", { className: "mt-2 text-sm text-ink-3", children: t('pages.focus.ruleBinding.separate') }), queue && _jsx("p", { className: "mt-2 text-[13px] text-ink-3", role: "status", "data-testid": "rule-binding-counts", children: t('pages.focus.ruleBinding.countsLine', { candidates: queue.summary.candidates, painGroups: queue.summary.painGroups, bound: queue.summary.bound, unlinked: queue.summary.unlinked, blocked: queue.summary.blocked }) }), queue?.truncated && _jsx("p", { className: "mt-1 text-[12.5px] text-amber", children: t('pages.focus.ruleBinding.truncatedNote', { count: queue.items.length }) }), error && _jsx("p", { role: "alert", className: "my-2 text-amber", children: error }), message && _jsx("p", { role: "status", className: "my-2", children: message }), visibleGroups.map(group => _jsxs("div", { className: "mt-4", "data-testid": "rule-binding-pain-group", children: [_jsxs("div", { className: "border-l-2 border-line pl-3", children: [_jsxs("div", { className: "text-[13px] text-ink-2 leading-relaxed", children: [_jsx("span", { className: "font-medium text-ink", children: t('pages.focus.ruleBinding.groupPrefix') }), ' ', group.pain.text
|
|
269
|
+
? splitReadingTitleBody(group.pain.text).title
|
|
270
|
+
: t('pages.focus.ruleBinding.painUnrecorded')] }), _jsxs("div", { className: "mt-0.5 text-[11.5px] text-ink-4", children: [painSourceLabel(t, group.pain.source), group.pain.createdAt ? ` · ${group.pain.createdAt.slice(0, 10)}` : '', ' · ', t('pages.focus.ruleBinding.groupCount', { count: group.items.length })] })] }), group.items.map(item => {
|
|
271
|
+
const cardState = deriveRuleBindingCardState(item);
|
|
272
|
+
const layout = splitReadingTitleBody(item.text);
|
|
273
|
+
const open = openCandidateId === item.candidateId;
|
|
274
|
+
return _jsxs("article", { className: "relative mt-3 pl-[22px] py-[14px] pr-[18px] bg-panel border border-line rounded-[6px]", "data-testid": `rule-binding-card-${item.candidateId}`, children: [_jsx("div", { className: `absolute left-0 top-0 bottom-0 w-[3px] rounded-l-[6px] ${RULE_BINDING_CARD_STATE_BAR[cardState]}` }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: `inline-flex items-center border rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase ${RULE_BINDING_CARD_STATE_CLASS[cardState]}`, children: t(RULE_BINDING_CARD_STATE_KEY[cardState]) }), (() => {
|
|
275
|
+
const stageLabel = stageKindLabel(t, item.generation?.stageKind ?? null);
|
|
276
|
+
return stageLabel
|
|
277
|
+
? _jsxs("span", { className: "text-[11.5px] text-ink-4", children: [t('pages.focus.ruleBinding.currentStage'), stageLabel] })
|
|
278
|
+
: null;
|
|
279
|
+
})()] }), _jsx("div", { className: "mt-[10px] mb-1 font-semibold text-ink leading-snug", "data-testid": `rule-binding-title-${item.candidateId}`, children: layout.title }), layout.body !== null && _jsx("div", { className: "mb-2 text-[14px] leading-relaxed text-ink-2 whitespace-pre-wrap break-words", children: layout.body }), item.binding.status === 'bound' && _jsxs("div", { className: "mt-2 px-3 py-2 bg-surface/60 border-l-2 border-gov text-[13px] leading-relaxed", children: [_jsx("span", { className: "text-ink-3", children: t('pages.focus.ruleBinding.boundTo') }), ' ', _jsx("span", { className: "text-ink", children: splitReadingTitleBody(item.binding.principle.text).title }), ' · ', _jsxs("span", { className: "text-ink-4", children: [t(`principles.status${item.binding.principle.status[0]?.toUpperCase()}${item.binding.principle.status.slice(1)}`, { defaultValue: item.binding.principle.status }), " \u00B7 ", t('pages.focus.ruleBinding.version'), " ", item.binding.principle.version] }), ' · ', _jsx(Link, { to: `/principles/${item.binding.principle.principleId}`, className: "text-gov underline", children: t('pages.focus.ruleBinding.viewPrinciple') })] }), cardState === 'bound_blocked' && item.generation && _jsxs("div", { className: "mt-2 text-[12.5px] leading-relaxed text-amber", "data-testid": `rule-binding-blocked-${item.candidateId}`, children: [t('pages.focus.ruleBinding.blockedLine', {
|
|
280
|
+
stage: stageKindLabel(t, item.generation.stageKind) ?? '',
|
|
281
|
+
reason: codeText(t, 'pages.focus.ruleBinding.blockReason', item.generation.reasonCode) || t('pages.focus.ruleBinding.blockReasonUnknown'),
|
|
282
|
+
}), _jsx("div", { className: "mt-0.5 text-ink-3", children: t('pages.focus.ruleBinding.blockedNoOwnerApproval') }), item.generation.detail && _jsxs("details", { className: "mt-1 text-[11.5px]", children: [_jsx("summary", { className: "cursor-pointer select-none", children: t('pages.focus.ruleBinding.blockedDetail') }), _jsx("pre", { className: "mt-1 max-h-40 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px]", children: item.generation.detail })] })] }), cardState === 'attention' && _jsx("div", { className: "mt-2 text-[12.5px] leading-relaxed text-amber", "data-testid": `rule-binding-attention-${item.candidateId}`, children: t('pages.focus.ruleBinding.attentionLine', { reason: item.binding.status === 'attention' ? item.binding.reason : '' }) }), _jsxs("div", { className: "mt-2 text-[12.5px] text-ink-3 leading-relaxed", "data-testid": `rule-binding-next-${item.candidateId}`, children: [_jsx("span", { className: "font-medium", children: t('pages.focus.ruleBinding.nextLabel') }), ' ', cardState === 'bound_blocked' && item.resume?.status === 'refused'
|
|
283
|
+
? t('pages.focus.ruleBinding.nextBoundBlockedNoResume', { reason: codeText(t, 'pages.focus.ruleBinding.resumeReason', item.resume.reason) })
|
|
284
|
+
: t(RULE_BINDING_NEXT_STEP_KEY[cardState])] }), _jsxs("details", { className: "mt-2 text-[11.5px] text-ink-4", children: [_jsx("summary", { className: "cursor-pointer select-none", children: t('pages.focus.ruleBinding.techDetails') }), _jsxs("dl", { className: "mt-1 font-mono text-[11px] break-all", children: [_jsxs("div", { children: ["candidateId: ", item.candidateId] }), _jsxs("div", { children: ["painId: ", item.pain.painId || '—'] }), item.binding.status === 'bound' && _jsxs("div", { children: ["bindingRevision: ", item.binding.principle.revision, " \u00B7 decidedAt: ", item.binding.principle.decidedAt || '—'] }), item.resume && _jsxs("div", { children: ["resume: ", item.resume.status, item.resume.reason ? ` (${item.resume.reason})` : '', item.resume.scribeTaskId ? ` · ${item.resume.scribeTaskId}` : ''] })] })] }), _jsx("button", { type: "button", className: "mt-3 inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", disabled: busy, "aria-expanded": open, onClick: () => { if (open)
|
|
285
|
+
closeReview();
|
|
286
|
+
else {
|
|
287
|
+
setSearch('');
|
|
288
|
+
setReason('');
|
|
289
|
+
void load(item.candidateId);
|
|
290
|
+
} }, children: open ? t('pages.focus.ruleBinding.closeReview') : t('pages.focus.ruleBinding.openReview') }), open && review && review.candidateId === item.candidateId && _jsxs("div", { className: "mt-4 border-t border-line pt-3", children: [_jsx("h3", { className: "font-medium text-[13px]", children: t('pages.focus.ruleBinding.candidate') }), _jsx("p", { className: "mt-1 whitespace-pre-wrap text-sm", children: review.candidateText }), _jsxs("p", { className: "mt-2 text-sm", children: [t('pages.focus.ruleBinding.pain'), "\uFF1A", review.sourcePain.text ?? t('pages.focus.ruleBinding.painUnrecorded')] }), _jsxs("details", { className: "mt-2 text-sm", children: [_jsx("summary", { children: t('pages.focus.ruleBinding.evidence') }), _jsx("pre", { className: "whitespace-pre-wrap break-words", children: review.evidence })] }), bound && review.boundPrinciple && _jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-3 px-3 py-2 bg-surface/60 border-l-2 border-gov text-[13px] leading-relaxed", children: [_jsx("div", { className: "whitespace-pre-wrap", children: review.boundPrinciple.text }), _jsxs("div", { className: "mt-1 text-ink-4", children: [t('pages.focus.ruleBinding.boundSince', { revision: review.boundPrinciple.revision, date: review.boundPrinciple.decidedAt.slice(0, 10) || '—' }), " \u00B7 ", _jsx(Link, { to: `/principles/${review.boundPrinciple.principleId}`, className: "text-gov underline", children: t('pages.focus.ruleBinding.viewPrinciple') })] })] }), canOfferRuleBindingResume({ ...item, resume: review.resume }) && _jsx("button", { type: "button", className: "mt-3 rounded border border-gov bg-gov text-paper px-3 py-2 text-sm disabled:opacity-50", disabled: busy, onClick: () => void revise(), children: t('pages.focus.ruleBinding.reviseCta') }), canOfferSourceCorrection({ ...item, sourceCorrection: review.sourceCorrection }) && _jsx("button", { type: "button", className: "mt-3 ml-2 rounded border border-gov bg-gov text-paper px-3 py-2 text-sm disabled:opacity-50", disabled: busy || !reason.trim(), "data-testid": "rule-binding-source-correct-cta", onClick: () => void correctSource(), children: t('pages.focus.ruleBinding.sourceCorrectionCta') }), review.resume?.status === 'refused' && _jsx("p", { className: "mt-3 text-[12.5px] text-ink-3 leading-relaxed", children: t('pages.focus.ruleBinding.resumeUnavailable', { reason: codeText(t, 'pages.focus.ruleBinding.resumeReason', review.resume.reason) }) }), review.generation?.stage === 'blocked' && review.sourceCorrection?.status === 'unavailable' && _jsx("p", { className: "mt-2 text-[12.5px] text-ink-4 leading-relaxed", children: t('pages.focus.ruleBinding.sourceCorrectionUnavailable', { reason: review.sourceCorrection.reason ?? '' }) }), review.generation?.stage === 'in_progress' && _jsx("p", { className: "mt-3 text-[12.5px] text-ink-3", children: t('pages.focus.ruleBinding.nextBoundGenerating') })] }), !bound && _jsxs(_Fragment, { children: [_jsx("p", { className: "mt-3 text-sm text-ink-3", children: t('pages.focus.ruleBinding.chooseTarget') }), _jsxs("form", { className: "mt-3 flex gap-2", onSubmit: event => { event.preventDefault(); void load(review.candidateId, search); }, children: [_jsx("input", { value: search, onChange: event => setSearch(event.target.value), maxLength: 200, "aria-label": t('pages.focus.ruleBinding.search'), className: "border border-line p-2 text-sm" }), _jsx("button", { disabled: busy, type: "submit", children: t('pages.focus.ruleBinding.search') })] }), review.eligibleTargets.length === 0 && _jsxs("p", { className: "mt-2 text-sm", children: [t('pages.focus.ruleBinding.noTarget'), " ", _jsx(Link, { to: "/principles", className: "underline", children: t('pages.focus.ruleBinding.formation') })] }), review.eligibleTargets.map(target => _jsxs("label", { className: "my-3 block border border-line p-3 text-sm", children: [_jsx("input", { type: "radio", name: "binding-target", value: target.principleId, checked: selectedId === target.principleId, onChange: () => setSelectedId(target.principleId) }), _jsxs("span", { className: "ml-2", children: [t(`principles.status${target.status[0]?.toUpperCase()}${target.status.slice(1)}`, { defaultValue: target.status }), " \u00B7 ", t('pages.focus.ruleBinding.version'), " ", target.version] }), _jsx("p", { className: "mt-2 whitespace-pre-wrap", children: target.text })] }, target.principleId)), _jsx("p", { className: "mt-3 text-sm text-ink-3", children: review.resumeNotice }), _jsx("p", { className: "mt-2 text-sm", children: t('pages.focus.ruleBinding.declaration') })] }), _jsxs("label", { className: "mt-3 block text-sm", children: [t('pages.focus.ruleBinding.reason'), _jsx("textarea", { className: "mt-1 block w-full border border-line p-2", maxLength: 2000, value: reason, onChange: event => setReason(event.target.value) })] }), _jsxs("div", { className: "mt-3 flex gap-2 flex-wrap", children: [!bound && _jsx("button", { type: "button", className: "rounded border border-gov px-3 py-2 text-sm disabled:opacity-50", disabled: busy || !reason.trim() || !selectedId, onClick: () => void decide('bind'), children: t('pages.focus.ruleBinding.bind') }), bound && _jsx("button", { type: "button", className: "rounded border border-line px-3 py-2 text-sm disabled:opacity-50", disabled: busy || !reason.trim(), onClick: () => void decide('revoke'), children: t('pages.focus.ruleBinding.revokeSecondary') })] }), _jsxs("p", { className: "mt-3 text-sm text-ink-3", children: [t('pages.focus.ruleBinding.revokeBoundary'), " ", _jsx(Link, { to: "/activation", className: "underline", children: t('pages.focus.ruleBinding.stop') })] })] })] }, item.candidateId);
|
|
291
|
+
})] }, group.key)), groups.length > COLLAPSED_GROUP_LIMIT && _jsx("button", { type: "button", className: "mt-3 text-[12.5px] text-gov underline", onClick: () => setShowAllGroups(value => !value), "aria-expanded": showAllGroups, "data-testid": "rule-binding-groups-toggle", children: showAllGroups
|
|
292
|
+
? t('pages.focus.ruleBinding.hideRemaining')
|
|
293
|
+
: t('pages.focus.ruleBinding.viewRemaining', { count: groups.length - COLLAPSED_GROUP_LIMIT }) })] });
|
|
68
294
|
}
|
|
@@ -6,3 +6,8 @@ export declare function validateBindingMutation(value: unknown): {
|
|
|
6
6
|
revisionStatus?: string;
|
|
7
7
|
nextAction?: string;
|
|
8
8
|
} | null;
|
|
9
|
+
export declare function validateBindingRevise(value: unknown): {
|
|
10
|
+
status: string;
|
|
11
|
+
reason?: string;
|
|
12
|
+
nextAction?: string;
|
|
13
|
+
} | null;
|