create-principles-disciple 1.145.35 → 1.145.37

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.
Files changed (81) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/console/dist/server/index.js +4 -0
  3. package/console/dist/server/models/ApprovalsConsoleModel.d.ts +22 -2
  4. package/console/dist/server/models/ApprovalsConsoleModel.js +56 -16
  5. package/console/dist/server/models/ApprovalsGroupedConsoleModel.d.ts +8 -0
  6. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +3 -2
  7. package/console/dist/server/models/approval-review-material.d.ts +15 -0
  8. package/console/dist/server/models/approval-review-material.js +32 -0
  9. package/console/dist/server/routes/approvals.js +32 -7
  10. package/console/dist/server/utils/response.js +8 -0
  11. package/console/dist/ui/api.d.ts +8 -2
  12. package/console/dist/ui/api.js +8 -4
  13. package/console/dist/ui/i18n/en.json +98 -7
  14. package/console/dist/ui/i18n/zh-CN.json +98 -7
  15. package/console/dist/ui/pages/focus/ApprovalSubjectReading.d.ts +24 -0
  16. package/console/dist/ui/pages/focus/ApprovalSubjectReading.js +133 -0
  17. package/console/dist/ui/pages/focus/FocusPage.d.ts +5 -26
  18. package/console/dist/ui/pages/focus/FocusPage.js +166 -459
  19. package/console/dist/ui/pages/focus/OwnerWorkbench.d.ts +19 -0
  20. package/console/dist/ui/pages/focus/OwnerWorkbench.js +75 -0
  21. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +4 -1
  22. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +7 -3
  23. package/console/dist/ui/pages/focus/focus-validation.js +31 -0
  24. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +110 -86
  25. package/console/dist/ui/pages/principles/PrinciplesPage.js +39 -30
  26. package/console/dist/ui/utils/approval-review.d.ts +30 -0
  27. package/console/dist/ui/utils/approval-review.js +50 -0
  28. package/console/dist/ui/utils/reading-layout.d.ts +1 -0
  29. package/console/dist/ui/utils/reading-layout.js +1 -1
  30. package/console/dist/ui/utils/validators.d.ts +4 -0
  31. package/console/dist/ui/utils/validators.js +10 -2
  32. package/console/dist/web/assets/app.css +119 -11
  33. package/console/dist/web/assets/app.js +40 -30
  34. package/core/dist/runtime-v2/activation/activation-types.d.ts +12 -2
  35. package/core/dist/runtime-v2/activation/activation-types.d.ts.map +1 -1
  36. package/core/dist/runtime-v2/activation/activation-types.js.map +1 -1
  37. package/core/dist/runtime-v2/activation/approval-queue.d.ts +3 -2
  38. package/core/dist/runtime-v2/activation/approval-queue.d.ts.map +1 -1
  39. package/core/dist/runtime-v2/activation/approval-queue.js +6 -4
  40. package/core/dist/runtime-v2/activation/approval-queue.js.map +1 -1
  41. package/core/dist/runtime-v2/activation/approval-review-guard.d.ts +26 -0
  42. package/core/dist/runtime-v2/activation/approval-review-guard.d.ts.map +1 -0
  43. package/core/dist/runtime-v2/activation/approval-review-guard.js +25 -0
  44. package/core/dist/runtime-v2/activation/approval-review-guard.js.map +1 -0
  45. package/core/dist/runtime-v2/activation/memory-approval-store.d.ts +6 -2
  46. package/core/dist/runtime-v2/activation/memory-approval-store.d.ts.map +1 -1
  47. package/core/dist/runtime-v2/activation/memory-approval-store.js +26 -2
  48. package/core/dist/runtime-v2/activation/memory-approval-store.js.map +1 -1
  49. package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts +6 -2
  50. package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts.map +1 -1
  51. package/core/dist/runtime-v2/activation/sqlite-approval-store.js +59 -2
  52. package/core/dist/runtime-v2/activation/sqlite-approval-store.js.map +1 -1
  53. package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts +24 -0
  54. package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts.map +1 -0
  55. package/core/dist/runtime-v2/diagnostician-association-shadow.js +84 -0
  56. package/core/dist/runtime-v2/diagnostician-association-shadow.js.map +1 -0
  57. package/core/dist/runtime-v2/diagnostician-association-validation.d.ts +18 -0
  58. package/core/dist/runtime-v2/diagnostician-association-validation.d.ts.map +1 -0
  59. package/core/dist/runtime-v2/diagnostician-association-validation.js +47 -0
  60. package/core/dist/runtime-v2/diagnostician-association-validation.js.map +1 -0
  61. package/core/dist/runtime-v2/diagnostician-output.d.ts +123 -0
  62. package/core/dist/runtime-v2/diagnostician-output.d.ts.map +1 -1
  63. package/core/dist/runtime-v2/diagnostician-output.js +41 -0
  64. package/core/dist/runtime-v2/diagnostician-output.js.map +1 -1
  65. package/core/dist/runtime-v2/index.d.ts +2 -0
  66. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  67. package/core/dist/runtime-v2/index.js +1 -0
  68. package/core/dist/runtime-v2/index.js.map +1 -1
  69. package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts +12 -1
  70. package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts.map +1 -1
  71. package/core/dist/runtime-v2/internalization/diag-router-runner.js +50 -2
  72. package/core/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
  73. package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts +45 -0
  74. package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts.map +1 -0
  75. package/core/dist/runtime-v2/internalization/router-context-evidence.js +64 -0
  76. package/core/dist/runtime-v2/internalization/router-context-evidence.js.map +1 -0
  77. package/core/package.json +1 -1
  78. package/package.json +1 -1
  79. package/pd-cli/package.json +1 -1
  80. package/plugin/dist/bundle.js +395 -395
  81. package/release-manager/package.json +1 -1
@@ -48,6 +48,97 @@
48
48
  },
49
49
  "pages": {
50
50
  "focus": {
51
+ "workbench": {
52
+ "textPreview": "Text preview",
53
+ "codePreview": "Rule implementation preview",
54
+ "fullText": "Expand complete text",
55
+ "fullCode": "Expand complete rule implementation",
56
+ "scope": "Applicable scope",
57
+ "compare": "Compare the previous version and retained intent",
58
+ "intentReviewed": "I checked the old and new versions and the intent to retain",
59
+ "promptConsequence": "Approval enters the existing prompt activation flow. Delivery still depends on host and capacity.",
60
+ "ruleConsequence": "Accepting an execution proposal is not promotion to live. Observation and promotion remain separate decisions.",
61
+ "approveOne": "Approve this revision",
62
+ "rejectOne": "Reject this revision",
63
+ "replaceOne": "Replace pending revision",
64
+ "replacementId": "Validated replacement artifact identifier",
65
+ "reason": "Reason for this decision",
66
+ "confirm": "Confirm submission",
67
+ "cancel": "Cancel",
68
+ "resultUnknown": "The submission result is unconfirmed. Read the current state before deciding again; do not resubmit.",
69
+ "notRecorded": "This decision is not complete. Review the refreshed material.",
70
+ "approved": "Approval is complete. Check activation and delivery in runtime status.",
71
+ "rejected": "This revision was rejected. Other subjects were not processed.",
72
+ "activationFailed": "Approval is complete, but activation failed. Use runtime recovery; do not approve again.",
73
+ "approvedRuntimeUnknown": "Approval was recorded. Activation is incomplete or unconfirmed. Check runtime status.",
74
+ "replaced": "The pending revision was replaced. Review the current text before deciding.",
75
+ "versionNeedsReview": "Revision {{index}} to review",
76
+ "title": "Needs your attention",
77
+ "subtitle": "Choose one item, understand the proposal and its consequences, then continue.",
78
+ "list": "Work items",
79
+ "count": "{{count}} items",
80
+ "uncertain": "Some sources could not be read. This list may be incomplete.",
81
+ "empty": "No work items are currently confirmed.",
82
+ "back": "Back to work items",
83
+ "next": "View next item",
84
+ "readingEnd": "This is the end of this item. Return to the list or review the next one.",
85
+ "completed": "Review outcome",
86
+ "notCurrent": "This item is no longer in the current list",
87
+ "startTitle": "Start with one item",
88
+ "startBody": "Choose an item to read its material, check the concerns, and see the available actions in one place.",
89
+ "notCurrentBody": "It may have been resolved, or its source may be unavailable. Use the freshly read state.",
90
+ "start": "Start review",
91
+ "loading": "Loading this approval’s exact material…",
92
+ "why": "What this is trying to solve",
93
+ "proposal": "What PD proposes to change",
94
+ "decision": "Your decision",
95
+ "proposedBehavior": "Proposed behavior (this draft)",
96
+ "currentApproved": "currently approved",
97
+ "currentRejected": "currently rejected",
98
+ "refusedCurrentState": "This operation was not performed; {{state}}.",
99
+ "currentStateConfirmed": "The record was reloaded: {{state}}. Whether this submission succeeded remains unconfirmed.",
100
+ "runtimeCheckLink": "Check activation and delivery on the runtime page.",
101
+ "sourceUnlinked": "This approval could not be linked to a principle or a readable name. Check the exact text and source first; you can reject it if the material is insufficient.",
102
+ "risksUnknown": "Risk notes for this revision are missing or unreadable. Risks remain unconfirmed.",
103
+ "risksEmpty": "This revision lists no risks; that does not establish safety.",
104
+ "approvalNote": "Approval note (optional)",
105
+ "budgetBasis": "Prompt capacity source and estimate basis",
106
+ "sourcesPending": "Sources unconfirmed",
107
+ "countExplanation": "How items are counted",
108
+ "archiveReview": "Defer or archive review",
109
+ "archiveConsequence": "Approving defers or archives this proposal; it does not activate a prompt or execution rule.",
110
+ "approveArchive": "Approve defer or archive",
111
+ "beforeDecision": "Before you decide",
112
+ "subjectUnconfirmed": "The complete decision material for this revision is unconfirmed. Check its source first.",
113
+ "revisionChanged": "The pending revision changed. Return to the list and read the new revision and intent comparison before deciding.",
114
+ "scopeUnknown": "Unspecified scope and exceptions remain unknown; they do not mean this applies in every situation.",
115
+ "principleLimits": "Principle-level risks and gaps (not validation of this revision)",
116
+ "reasonAndScope": "Rationale and consequences",
117
+ "materialUnavailable": "The material for this item is unavailable.",
118
+ "exactMaterialMissing": "No text matching this approval and revision was found. Check the material first.",
119
+ "versionLock": "You can read the material. The current approval service does not yet guarantee that the submitted version is the one you reviewed, so approve, reject, and replace actions are unavailable here.",
120
+ "openPrinciple": "View principle details and existing records",
121
+ "subjectDetails": "View approval and revision identifiers",
122
+ "permissionsUnknown": "Approval permissions could not be confirmed. Reading remains available; decision actions are locked.",
123
+ "taskDecision": "Current proposal decision",
124
+ "revisionDecision": "Proposal revision decision",
125
+ "validationReview": "Behavior validation review",
126
+ "approvalMaterialReview": "Approval material check",
127
+ "unknownReview": "Item type needs checking",
128
+ "promptReview": "Prompt principle review",
129
+ "ruleReview": "Execution proposal review",
130
+ "approvalSummary": "Check the proposed behavior, scope, and exact revision.",
131
+ "bindingReview": "Rule attribution review",
132
+ "bindingSummary": "Confirm which principle this rule should implement.",
133
+ "decisionRecorded": "The service received the decision. The list is refreshing from current state.",
134
+ "retry": "Read again",
135
+ "runtimeRecords": "Runtime and historical records",
136
+ "rulePromotion": "Rule promotion review",
137
+ "promotionSummary": "This rule is still being observed. Review the safety evidence before continuing observation, rejecting, or promoting it.",
138
+ "openActivation": "View rule runtime and emergency controls",
139
+ "safetyAlert": "{{count}} rules are safety-isolated. Check their runtime state.",
140
+ "approvalsUnavailable": "Some approval material could not be read. Other confirmed items remain available."
141
+ },
51
142
  "ruleBinding": {
52
143
  "title": "Formal principle attribution for rules",
53
144
  "separate": "Attribution only states which principle a rule belongs to; it does not approve the rule, enable it, or replace a development design approval.",
@@ -522,13 +613,7 @@
522
613
  },
523
614
  "conclusion": {
524
615
  "sectionTitle": "Current status",
525
- "allClear": "Nothing needs your attention right now.",
526
- "pendingTotal": "{{count}} item(s) waiting on you",
527
- "actionableUnlocked": "{{count}} of them actionable now",
528
- "actionableLocked": "{{count}} actionable object(s) — governance actions are locked, see the notice above",
529
- "countsNote": "Scope: \"waiting on you\" = actionable objects on screen + inbox pending items (decisions / individually-attended blocked / recovery) not already shown; each object counts once; historical unknowns stay explanatory and are not counted.",
530
- "pendingUncertain": "Pending count cannot be confirmed — decision updates are unavailable.",
531
- "actionableWithoutTotal": "{{count}} item(s) actionable now",
616
+ "countsNote": "Scope: the count lists the work items in this workbench; blocked and recovery items are listed separately below.",
532
617
  "degradedLabel": "Decision updates degraded:"
533
618
  },
534
619
  "ownerAttention": {
@@ -756,6 +841,9 @@
756
841
  "metricLatest": "Last Updated",
757
842
  "blockBasis": "Formation Basis",
758
843
  "blockBasisFallback": "This principle was distilled from historical behavior calibration. Its detailed source is not yet linked.",
844
+ "contextDisclosure": "Formation and other information",
845
+ "libraryOverview": "Principle overview",
846
+ "openDetails": "View principle details",
759
847
  "blockImpact": "Current Impact",
760
848
  "blockImpactSeeDecisionView": "Whether this currently shapes behavior is shown on the detail page under current enforcement — this card does not assert impact from lifecycle status alone.",
761
849
  "blockImpactActive": "This principle is currently shaping the Agent's subsequent behavior.",
@@ -812,6 +900,9 @@
812
900
  "detail": {
813
901
  "loading": "Loading…",
814
902
  "backToList": "← Back to list",
903
+ "formationDisclosure": "How this principle formed",
904
+ "fullRule": "Read the complete principle",
905
+ "extraContext": "Other source notes and reflection questions",
815
906
  "conclusion": "Conclusion",
816
907
  "policyNote": "This is a behavioral policy for future similar scenarios. You can approve, modify, reject, or park it.",
817
908
  "modifyWording": "Modify Wording",
@@ -48,6 +48,97 @@
48
48
  },
49
49
  "pages": {
50
50
  "focus": {
51
+ "workbench": {
52
+ "textPreview": "正文预览",
53
+ "codePreview": "规则实现预览",
54
+ "fullText": "展开完整正文",
55
+ "fullCode": "展开完整规则实现",
56
+ "scope": "适用范围",
57
+ "compare": "对照上一版本与保留的原意",
58
+ "intentReviewed": "我已核对前后版本与需要保留的原意",
59
+ "promptConsequence": "批准后进入现有提示原则激活流程;实际投递仍受宿主与容量限制。",
60
+ "ruleConsequence": "批准执行方案不等于正式上线,观察与上线决定仍然独立。",
61
+ "approveOne": "批准这个版本",
62
+ "rejectOne": "拒绝这个版本",
63
+ "replaceOne": "替换待审版本",
64
+ "replacementId": "已验证的新草案标识",
65
+ "reason": "请说明理由",
66
+ "confirm": "确认提交",
67
+ "cancel": "取消",
68
+ "resultUnknown": "提交结果尚未确认,请重新读取状态后再决定,勿重复提交。",
69
+ "notRecorded": "这次决定尚未完成,请按最新材料核对。",
70
+ "approved": "已批准。实际激活与投递请查看生效情况。",
71
+ "rejected": "已拒绝这个版本,其他对象未被处理。",
72
+ "activationFailed": "审批已完成,但激活失败。请查看生效情况与恢复入口,不要重复批准。",
73
+ "approvedRuntimeUnknown": "审批已记录,激活尚未完成或暂不可确认。请查看生效情况。",
74
+ "replaced": "已替换待审版本。请重新核对当前正文后决定。",
75
+ "versionNeedsReview": "待核对版本 {{index}}",
76
+ "title": "需要你处理",
77
+ "subtitle": "选一件事,看清建议和决定后果,再继续下一件。",
78
+ "list": "待处理事项",
79
+ "count": "{{count}} 项",
80
+ "uncertain": "部分资料暂未读取,当前列表可能不完整。",
81
+ "empty": "当前没有可确认的待处理事项。",
82
+ "back": "返回事项列表",
83
+ "next": "查看下一件",
84
+ "readingEnd": "本件材料到这里结束。你可以返回列表,或查看下一件。",
85
+ "completed": "处理结果",
86
+ "notCurrent": "这件事已不在当前列表",
87
+ "startTitle": "从一件事开始",
88
+ "startBody": "先选择一个待办。这里会集中显示它的材料、需要核对的地方和可执行操作。",
89
+ "notCurrentBody": "它可能已处理,或当前资料暂不可用。请以重新读取后的状态为准。",
90
+ "start": "开始处理",
91
+ "loading": "正在读取这次审批的准确材料…",
92
+ "why": "这次想解决什么",
93
+ "proposal": "PD 建议怎样改变",
94
+ "decision": "本次决定",
95
+ "proposedBehavior": "拟议行为(来自本次草案)",
96
+ "currentApproved": "当前已批准",
97
+ "currentRejected": "当前已拒绝",
98
+ "refusedCurrentState": "本次操作未执行;{{state}}。",
99
+ "currentStateConfirmed": "已重新读取:{{state}}。本次提交是否成功暂无法确认。",
100
+ "runtimeCheckLink": "实际激活与投递请查看生效情况。",
101
+ "sourceUnlinked": "尚未解析出这项审批对应的原则,也没有找到可读的名称。请先核对准确正文与来源;材料不足时可以拒绝。",
102
+ "risksUnknown": "这份版本的风险说明尚未记录或暂不可读,风险仍需核对。",
103
+ "risksEmpty": "这份版本未列出风险;这不代表已验证安全。",
104
+ "approvalNote": "批准备注(可选)",
105
+ "budgetBasis": "查看提示词容量的来源与估算依据",
106
+ "sourcesPending": "资料待确认",
107
+ "countExplanation": "这些事项如何计数",
108
+ "archiveReview": "暂缓与归档审查",
109
+ "archiveConsequence": "同意后会将这项提案暂缓或归档,不会启用提示词或执行规则。",
110
+ "approveArchive": "同意暂缓或归档",
111
+ "beforeDecision": "决定前需要知道",
112
+ "subjectUnconfirmed": "尚未确认这份版本对应的完整决策材料,请先核对来源。",
113
+ "revisionChanged": "待审版本已变化。请返回列表重新读取,核对新版与原意后再决定。",
114
+ "scopeUnknown": "未写明的适用范围或例外仍属未知,不应理解为适用于所有情况。",
115
+ "principleLimits": "查看原则层面的风险与已知缺口(不代表本次版本已验证)",
116
+ "reasonAndScope": "为什么这样建议,以及决定后果",
117
+ "materialUnavailable": "这次事项的材料暂不可用。",
118
+ "exactMaterialMissing": "尚未找到与这个审批对象和版本一致的正文,请先核对材料。",
119
+ "versionLock": "材料可以阅读。当前审批服务尚未保证提交的就是你看过的版本,因此这里暂不提供批准、拒绝或替换操作。",
120
+ "openPrinciple": "查看原则详情与现有记录",
121
+ "subjectDetails": "查看审批对象与版本标识",
122
+ "permissionsUnknown": "暂时无法确认审批权限。可以阅读材料,决定操作暂不可用。",
123
+ "taskDecision": "本轮方案决定",
124
+ "revisionDecision": "方案修订决定",
125
+ "validationReview": "行为验证评审",
126
+ "approvalMaterialReview": "审批材料核对",
127
+ "unknownReview": "事项类型待核对",
128
+ "promptReview": "提示原则审查",
129
+ "ruleReview": "执行方案审查",
130
+ "approvalSummary": "核对这次准备改变的行为、范围与具体版本。",
131
+ "bindingReview": "规则归属核对",
132
+ "bindingSummary": "确认这条规则应当落实哪条原则。",
133
+ "decisionRecorded": "服务已接收本次决定,列表正在按最新状态更新。",
134
+ "retry": "重新读取",
135
+ "runtimeRecords": "运行与历史记录",
136
+ "rulePromotion": "规则上线审查",
137
+ "promotionSummary": "这条规则仍在观察中。请核对安全证据,再决定继续观察、拒绝或上线。",
138
+ "openActivation": "查看规则运行与紧急控制",
139
+ "safetyAlert": "{{count}} 条规则已被安全隔离,请关注运行状态。",
140
+ "approvalsUnavailable": "部分审批材料未能读取,其他已知事项仍可查看。"
141
+ },
51
142
  "ruleBinding": {
52
143
  "title": "规则的正式原则归属",
53
144
  "separate": "关联仅声明规则属于哪条原则;不批准规则、不启用规则,也不代替开发设计批准。",
@@ -522,13 +613,7 @@
522
613
  },
523
614
  "conclusion": {
524
615
  "sectionTitle": "当前状态",
525
- "allClear": "当前没有需要你处理的治理事项。",
526
- "pendingTotal": "待你处理 {{count}} 项",
527
- "actionableUnlocked": "其中现在可操作 {{count}} 项",
528
- "actionableLocked": "可操作对象 {{count}} 项——治理动作已锁定,原因见上方说明",
529
- "countsNote": "口径:「待你处理」= 屏上可操作对象 + 决定动态中未上屏的待决定/被阻塞(需逐条关注)/需要恢复事项;同一对象只计一次;历史未知项只作说明,不计入。",
530
- "pendingUncertain": "待处理数暂不可确认——决定动态不可用。",
531
- "actionableWithoutTotal": "现在可操作 {{count}} 项",
616
+ "countsNote": "口径:计数为本页工作台列出的可处理事项数;被阻塞与需要恢复的事项在下方单独列出。",
532
617
  "degradedLabel": "决定动态降级:"
533
618
  },
534
619
  "ownerAttention": {
@@ -756,6 +841,9 @@
756
841
  "metricLatest": "最近更新",
757
842
  "blockBasis": "形成依据",
758
843
  "blockBasisFallback": "该原则由历史行为校准沉淀而来,详细来源尚未关联。",
844
+ "contextDisclosure": "形成过程与其他信息",
845
+ "libraryOverview": "原则概况",
846
+ "openDetails": "查看原则详情",
759
847
  "blockImpact": "当前影响",
760
848
  "blockImpactSeeDecisionView": "当前是否影响行为,以详情页「当前实际执行」为准(本卡片不据生命周期状态断言影响)。",
761
849
  "blockImpactActive": "该原则正在影响智能体后续行为。",
@@ -812,6 +900,9 @@
812
900
  "detail": {
813
901
  "loading": "加载中…",
814
902
  "backToList": "← 返回列表",
903
+ "formationDisclosure": "这条原则如何形成",
904
+ "fullRule": "展开完整原则正文",
905
+ "extraContext": "其他来源说明与回顾问题",
815
906
  "conclusion": "结论",
816
907
  "policyNote": "这是面向未来相似场景的行为政策,可批准、修改、拒绝或暂存。",
817
908
  "modifyWording": "修改措辞",
@@ -0,0 +1,24 @@
1
+ import type { ApprovalsGroupedData } from '../../api.js';
2
+ export declare function ApprovalSubjectReading({ principleId, approvalId, artifactId, channel, lockedReason, targetHost, onResolved, revisionReview, revisionReviewUnavailable, allowedActions, budgetNotice, promptInjection, sourceUnlinked, showPrincipleLink }: {
3
+ principleId: string;
4
+ approvalId: string;
5
+ artifactId: string;
6
+ channel: string;
7
+ lockedReason?: string;
8
+ targetHost?: 'openclaw' | 'codex' | 'workbuddy';
9
+ onResolved?: (message: string) => void;
10
+ revisionReview?: {
11
+ artifactId?: string;
12
+ artifactDigest?: string;
13
+ oldStatement: string;
14
+ newStatement: string;
15
+ intentFields: Record<string, string>;
16
+ };
17
+ revisionReviewUnavailable?: boolean;
18
+ /** Backend-assessed actions for THIS approval; absent means no per-action gate (the guarded submit still applies). */
19
+ allowedActions?: readonly ('approve' | 'reject' | 'edit_approval')[];
20
+ budgetNotice?: string;
21
+ promptInjection?: ApprovalsGroupedData['promptInjection'];
22
+ sourceUnlinked?: boolean;
23
+ showPrincipleLink?: boolean;
24
+ }): import("react").JSX.Element;
@@ -0,0 +1,133 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Link, useLocation } from 'react-router-dom';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { fetchApprovalReview, fetchOwnerDecisionView, approveApproval, rejectApproval, editApproval, fetchAllActivations } from '../../api.js';
6
+ import { localizeApprovalWarning } from '../../utils/approval-warning-localization.js';
7
+ import { truncateByCodePoints } from '../../utils/reading-layout.js';
8
+ export function ApprovalSubjectReading({ principleId, approvalId, artifactId, channel, lockedReason, targetHost, onResolved, revisionReview, revisionReviewUnavailable, allowedActions, budgetNotice, promptInjection, sourceUnlinked, showPrincipleLink = true }) {
9
+ const { t } = useTranslation();
10
+ const location = useLocation();
11
+ const [review, setReview] = useState(null);
12
+ const [view, setView] = useState(null);
13
+ const [viewSettled, setViewSettled] = useState(false);
14
+ const [error, setError] = useState('');
15
+ const [resultText, setResultText] = useState('');
16
+ const [loading, setLoading] = useState(true);
17
+ const [busy, setBusy] = useState(false);
18
+ const [mode, setMode] = useState(null);
19
+ const [reason, setReason] = useState('');
20
+ const [newArtifact, setNewArtifact] = useState('');
21
+ const [intentReviewed, setIntentReviewed] = useState(false);
22
+ const [approveNote, setApproveNote] = useState('');
23
+ const epoch = useRef(0);
24
+ const read = useCallback(async () => {
25
+ const current = ++epoch.current;
26
+ setReview(null);
27
+ setLoading(true);
28
+ setIntentReviewed(false);
29
+ setError('');
30
+ const result = await fetchApprovalReview(approvalId);
31
+ if (current !== epoch.current)
32
+ return null;
33
+ setLoading(false);
34
+ if (result.success) {
35
+ setReview(result.data);
36
+ return result.data;
37
+ }
38
+ setError([result.error, result.nextAction].filter(Boolean).join(' '));
39
+ return null;
40
+ }, [approvalId]);
41
+ useEffect(() => {
42
+ setError('');
43
+ setResultText('');
44
+ setView(null);
45
+ setViewSettled(false);
46
+ setMode(null);
47
+ setReason('');
48
+ setNewArtifact('');
49
+ setApproveNote('');
50
+ let current = true;
51
+ void read();
52
+ void fetchOwnerDecisionView(principleId).then(result => { if (current) {
53
+ setView(result.success ? result.data : null);
54
+ setViewSettled(true);
55
+ } });
56
+ return () => { current = false; epoch.current++; };
57
+ }, [principleId, approvalId, artifactId, channel, read]);
58
+ const narrative = (field) => field.status === 'known' ? field.value.map(item => item.text).join('\n\n') : field.reason.ownerText;
59
+ const subject = view?.decisionSubjects.find(item => item.key === approvalId && item.channel === channel
60
+ && item.revisionArtifactId.status === 'known' && item.revisionArtifactId.value === review?.expectedReview.artifactId);
61
+ const materialBlocks = subject?.materialGates.find(gate => gate.actionSemantic === 'approve')?.requiredItems.filter(item => item.status !== 'satisfied') ?? [];
62
+ const revisionMatches = !revisionReview || (review?.expectedReview.artifactState === 'present' && review.expectedReview.artifactId === artifactId
63
+ && revisionReview.artifactId === review.expectedReview.artifactId && revisionReview.artifactDigest === review.expectedReview.artifactDigest);
64
+ const approveReady = review?.approval.status === 'pending' && subject !== undefined && review.expectedReview.artifactState === 'present' && review.material.exactBody !== null && review.material.availability === 'available' && revisionMatches && !revisionReviewUnavailable && materialBlocks.length === 0;
65
+ const may = (semantic) => allowedActions === undefined || allowedActions.includes(semantic);
66
+ async function submit(action) {
67
+ if (!review || busy || lockedReason || review.approval.status !== 'pending')
68
+ return;
69
+ if (!may(action === 'approve' ? 'approve' : action === 'reject' ? 'reject' : 'edit_approval'))
70
+ return;
71
+ if (action === 'approve' && (!approveReady || (revisionReview && !intentReviewed)))
72
+ return;
73
+ if (action !== 'approve' && !reason.trim())
74
+ return;
75
+ if (action === 'edit' && !newArtifact.trim())
76
+ return;
77
+ const snapshot = review;
78
+ const current = epoch.current;
79
+ setBusy(true);
80
+ setError('');
81
+ setResultText('');
82
+ const result = action === 'approve' ? await approveApproval(approvalId, approveNote.trim() || undefined, { expectedReview: snapshot.expectedReview, targetHost,
83
+ ...(revisionReview ? { intentReviewed, reviewedArtifactId: snapshot.expectedReview.artifactId } : {}) })
84
+ : action === 'reject' ? await rejectApproval(approvalId, reason.trim(), snapshot.expectedReview)
85
+ : await editApproval(approvalId, newArtifact.trim(), { reason: reason.trim(), expectedReview: snapshot.expectedReview });
86
+ if (current !== epoch.current)
87
+ return;
88
+ const fresh = await read();
89
+ setBusy(false);
90
+ if (!fresh) {
91
+ setError(t('pages.focus.workbench.resultUnknown'));
92
+ return;
93
+ }
94
+ if (fresh.approval.status === 'approved' || fresh.approval.status === 'rejected') {
95
+ const state = t(fresh.approval.status === 'approved' ? 'pages.focus.workbench.currentApproved' : 'pages.focus.workbench.currentRejected');
96
+ const ownOutcome = result.success && ((action === 'approve' && fresh.approval.status === 'approved') || (action === 'reject' && fresh.approval.status === 'rejected'));
97
+ const refused = !result.success && result.status !== undefined && result.status >= 400 && result.status < 500;
98
+ let message = ownOutcome ? t(fresh.approval.status === 'approved' ? 'pages.focus.workbench.approved' : 'pages.focus.workbench.rejected')
99
+ : refused ? t('pages.focus.workbench.refusedCurrentState', { state }) + ' ' + [result.error, result.nextAction].filter(Boolean).join(' ')
100
+ : t('pages.focus.workbench.currentStateConfirmed', { state });
101
+ if (fresh.approval.status === 'approved' && !result.success && !refused) {
102
+ const activations = await fetchAllActivations(targetHost);
103
+ if (epoch.current !== current + 1)
104
+ return;
105
+ const active = activations.success && activations.data.activations.some(item => item.artifactId === fresh.approval.artifactId && item.status === 'active');
106
+ message += ' ' + t(active ? 'pages.focus.workbench.runtimeCheckLink' : result.reason === 'activation_failed' ? 'pages.focus.workbench.activationFailed' : 'pages.focus.workbench.approvedRuntimeUnknown');
107
+ }
108
+ if (result.success && result.data.warning) {
109
+ const warning = localizeApprovalWarning(result.data.warning, t);
110
+ message += ` ${warning.title} ${warning.body ?? warning.detail}`;
111
+ }
112
+ setResultText(message);
113
+ onResolved?.(message);
114
+ return;
115
+ }
116
+ if (!result.success)
117
+ setError(t(result.status === undefined ? 'pages.focus.workbench.resultUnknown' : 'pages.focus.workbench.notRecorded') + ' ' + [result.error, result.nextAction].filter(Boolean).join(' '));
118
+ else if (action === 'edit') {
119
+ setResultText(t('pages.focus.workbench.replaced'));
120
+ setMode(null);
121
+ setReason('');
122
+ setNewArtifact('');
123
+ setApproveNote('');
124
+ }
125
+ }
126
+ const body = review?.material.exactBody;
127
+ const bodyText = body?.text ?? review?.material.statement ?? '';
128
+ return _jsxs("div", { className: "space-y-7", children: [_jsxs("section", { children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.why') }), sourceUnlinked && _jsx("p", { role: "alert", className: "mb-3 text-sm leading-relaxed text-amber", children: t('pages.focus.workbench.sourceUnlinked') }), _jsx("p", { className: "whitespace-pre-wrap text-[14px] leading-relaxed text-ink-2", children: view ? narrative(view.incidentSummary) : t('pages.focus.workbench.materialUnavailable') })] }), _jsxs("section", { children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.proposal') }), loading ? _jsx("p", { role: "status", className: "text-sm text-ink-3", children: t('pages.focus.workbench.loading') }) : _jsxs(_Fragment, { children: [bodyText ? _jsxs(_Fragment, { children: [_jsx("p", { className: "mb-2 text-[12px] text-ink-4", children: t(body?.kind === 'rule_code' ? 'pages.focus.workbench.codePreview' : 'pages.focus.workbench.textPreview') }), _jsx("p", { className: "whitespace-pre-wrap break-words text-[15px] leading-[1.85] text-ink", children: truncateByCodePoints(bodyText, 240) }), _jsxs("details", { className: "mt-4 rounded-[4px] border border-line px-4 py-3 text-[13px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t(body?.kind === 'rule_code' ? 'pages.focus.workbench.fullCode' : 'pages.focus.workbench.fullText') }), _jsx("div", { className: "mt-4 whitespace-pre-wrap break-words text-[15px] leading-[1.85] text-ink-2", children: bodyText })] })] }) : _jsx("p", { className: "text-sm text-amber", children: t('pages.focus.workbench.exactMaterialMissing') }), review?.material.scope.length ? _jsxs("div", { className: "mt-4", children: [_jsx("h4", { className: "text-sm font-medium text-ink", children: t('pages.focus.workbench.scope') }), _jsx("ul", { className: "mt-2 list-disc space-y-1 pl-5 text-[13px] leading-relaxed text-ink-2", children: review.material.scope.map((scope, index) => _jsx("li", { children: scope }, index)) })] }) : null, review?.material.rationale && _jsxs("details", { className: "mt-4 text-[13px] text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.reasonAndScope') }), _jsx("p", { className: "mt-3 whitespace-pre-wrap leading-relaxed", children: review.material.rationale })] }), revisionReview && _jsxs("details", { className: "mt-4 text-[13px] leading-relaxed text-ink-2", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.compare') }), _jsx("p", { className: "mt-3 whitespace-pre-wrap", children: revisionReview.oldStatement }), _jsx("p", { className: "mt-3 whitespace-pre-wrap", children: revisionReview.newStatement }), _jsx("dl", { className: "mt-3", children: Object.entries(revisionReview.intentFields).map(([key, value]) => _jsxs("div", { children: [_jsx("dt", { className: "font-medium", children: t(`pages.focus.intentField.${key}`, { defaultValue: key }) }), _jsx("dd", { children: value })] }, key)) })] })] })] }), _jsxs("section", { className: "border-t border-line pt-5", "data-testid": "approval-review-limits", children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.beforeDecision') }), viewSettled && !subject && _jsx("p", { className: "text-sm leading-relaxed text-amber", children: t('pages.focus.workbench.subjectUnconfirmed') }), review && _jsx("div", { className: "mb-3 text-sm leading-relaxed text-ink-2", "data-testid": "approval-exact-risks", children: review.material.risks?.length ? _jsx("ul", { className: "list-disc space-y-1 pl-5", children: review.material.risks.map((risk, index) => _jsx("li", { children: risk }, index)) }) : _jsx("p", { children: t(review.material.risks === null ? 'pages.focus.workbench.risksUnknown' : 'pages.focus.workbench.risksEmpty') }) }), materialBlocks.map(item => _jsx("p", { className: "mb-2 text-sm leading-relaxed text-amber", children: item.ownerText }, item.key)), !revisionMatches && _jsx("p", { role: "alert", className: "mb-2 text-sm text-amber", children: t('pages.focus.workbench.revisionChanged') }), revisionReviewUnavailable && _jsx("p", { role: "alert", className: "mb-2 text-sm text-amber", children: t('pages.focus.revisionReviewUnavailable') }), budgetNotice && channel === 'prompt' && _jsx("p", { role: "alert", className: "mb-2 text-sm leading-relaxed text-amber", "data-testid": `approve-queue-badge-${principleId}`, children: budgetNotice }), promptInjection && channel === 'prompt' && _jsxs("details", { className: "mb-3 text-[12px] leading-relaxed text-ink-3", "data-testid": `approve-queue-badge-scope-${principleId}`, children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.budgetBasis') }), _jsxs("p", { className: "mt-2", children: [promptInjection.route === 'shared_render' ? t('pages.focus.promptScopeShared') : t('pages.focus.promptScopeList', { full: promptInjection.fullRenderChars !== undefined ? t('pages.focus.promptScopeFullRender', { count: promptInjection.fullRenderChars }) : '' }), promptInjection.hostKind ? ` · ${promptInjection.hostKind}` : ''] }), _jsx("p", { className: "mt-2", children: promptInjection.budgetCapacityStatus === 'confirmed' && promptInjection.budgetContextWindowTokens !== undefined
129
+ ? promptInjection.budgetObservedAt !== undefined ? t('pages.focus.promptBudgetObservedNote', { source: t(promptInjection.budgetCapacitySource === 'pi_catalog_exact' ? 'pages.focus.budgetCapacitySourcePiCatalog' : promptInjection.budgetCapacitySource === 'operator_declared' ? 'pages.focus.budgetCapacitySourceOperatorDeclared' : 'pages.focus.budgetCapacitySourceHostWindow'), window: promptInjection.budgetContextWindowTokens, observedAt: promptInjection.budgetObservedAt })
130
+ : t('pages.focus.promptBudgetModelAwareNote', { window: promptInjection.budgetContextWindowTokens, sourceSuffix: promptInjection.budgetCapacitySource === 'operator_declared' ? t('pages.focus.budgetCapacitySourceOperatorDeclaredSuffix') : '' })
131
+ : promptInjection.budgetObservedAt !== undefined ? t('pages.focus.promptBudgetFallbackObservedNote', { reason: t(`pages.focus.budgetFallbackReason.${promptInjection.budgetFallbackReason}`, { defaultValue: promptInjection.budgetFallbackReason ?? 'unknown' }), observedAt: promptInjection.budgetObservedAt })
132
+ : t('pages.focus.promptBudgetFallbackNote', { reason: promptInjection.budgetFallbackReason ? t('pages.focus.promptBudgetFallbackReasonSuffix', { code: t(`pages.focus.budgetFallbackReason.${promptInjection.budgetFallbackReason}`, { defaultValue: promptInjection.budgetFallbackReason }) }) : '' }) })] }), review?.material.targetBehavior && _jsxs("div", { className: "mb-3 text-[14px] leading-relaxed text-ink-2", "data-testid": "approval-exact-target-behavior", children: [_jsx("h4", { className: "mb-1 font-medium", children: t('pages.focus.workbench.proposedBehavior') }), _jsx("p", { className: "whitespace-pre-wrap break-words", children: review.material.targetBehavior })] }), subject && _jsx("p", { className: "text-sm leading-relaxed text-ink-2", children: narrative(subject.decisionMaterial.consequence) }), _jsx("p", { className: "mt-2 text-[13px] leading-relaxed text-ink-3", children: t('pages.focus.workbench.scopeUnknown') }), view && _jsxs("details", { className: "mt-3 text-[13px] leading-relaxed text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.principleLimits') }), _jsx("p", { className: "mt-3", children: view.nonApplicabilityOrUnknown.status === 'known' ? view.nonApplicabilityOrUnknown.value.map(item => item.text).join(' ') : view.nonApplicabilityOrUnknown.reason.ownerText }), _jsx("p", { className: "mt-2", children: view.risk.status === 'known' ? view.risk.value?.items.map(item => item.text).join(' ') || t('pages.principles.detail.ownerDecision.riskNotAssessed') : view.risk.reason?.ownerText }), _jsx("p", { className: "mt-2", children: view.uncertainty.status === 'known' ? view.uncertainty.value.map(item => item.text).join(' ') : view.uncertainty.reason.ownerText })] })] }), _jsxs("section", { className: "border-t border-line pt-5", children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.decision') }), lockedReason && _jsx("p", { role: "alert", className: "mb-3 text-[13px] leading-relaxed text-amber", children: lockedReason }), error && _jsx("p", { role: "alert", className: "mb-3 text-[13px] leading-relaxed text-amber", children: error }), resultText && _jsx("p", { role: "status", className: "mb-3 text-sm leading-relaxed text-ink", children: resultText }), !review && !loading && _jsx("button", { type: "button", className: "text-sm text-gov underline", onClick: () => { void read(); }, children: t('pages.focus.workbench.retry') }), review?.approval.status === 'pending' && _jsxs(_Fragment, { children: [_jsxs("details", { className: "mb-4 text-sm text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.approvalNote') }), _jsxs("label", { className: "mt-3 block", children: [t('pages.focus.workbench.approvalNote'), _jsx("textarea", { value: approveNote, maxLength: 2000, onChange: event => setApproveNote(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] })] }), revisionReview && _jsxs("label", { className: "mb-4 block text-sm text-ink-2", children: [_jsx("input", { type: "checkbox", checked: intentReviewed, onChange: event => setIntentReviewed(event.target.checked), className: "mr-2" }), t('pages.focus.workbench.intentReviewed')] }), _jsx("p", { className: "mb-4 text-[12.5px] leading-relaxed text-ink-3", children: t(channel === 'code_tool_hook' ? 'pages.focus.workbench.ruleConsequence' : channel === 'defer_archive' ? 'pages.focus.workbench.archiveConsequence' : 'pages.focus.workbench.promptConsequence') }), _jsxs("div", { className: "flex flex-wrap gap-3", children: [_jsx("button", { type: "button", disabled: !approveReady || busy || Boolean(lockedReason) || !may('approve') || Boolean(revisionReview && !intentReviewed), "data-testid": `approve-btn-${principleId}`, onClick: () => { void submit('approve'); }, className: "rounded-[4px] border border-gov bg-gov px-4 py-2.5 text-[13px] text-paper disabled:opacity-50", children: t(channel === 'defer_archive' ? 'pages.focus.workbench.approveArchive' : 'pages.focus.workbench.approveOne') }), _jsx("button", { type: "button", disabled: busy || Boolean(lockedReason) || !may('reject'), onClick: () => setMode('reject'), className: "rounded-[4px] border border-line px-4 py-2.5 text-[13px] text-ink disabled:opacity-50", children: t('pages.focus.workbench.rejectOne') }), _jsx("button", { type: "button", "data-testid": `edit-btn-${principleId}`, disabled: busy || Boolean(lockedReason) || !may('edit_approval') || review.expectedReview.artifactState === 'missing', onClick: () => setMode('edit'), className: "text-[13px] text-gov underline disabled:opacity-50", children: t('pages.focus.workbench.replaceOne') })] }), mode && _jsxs("form", { className: "mt-4 space-y-3", onSubmit: event => { event.preventDefault(); void submit(mode); }, children: [mode === 'edit' && _jsxs("label", { className: "block text-sm text-ink-2", children: [t('pages.focus.workbench.replacementId'), _jsx("input", { value: newArtifact, onChange: event => setNewArtifact(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] }), _jsxs("label", { className: "block text-sm text-ink-2", children: [t('pages.focus.workbench.reason'), _jsx("textarea", { value: reason, maxLength: 2000, onChange: event => setReason(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] }), _jsx("button", { type: "submit", disabled: busy || !reason.trim() || (mode === 'edit' && !newArtifact.trim()), className: "rounded border border-line px-4 py-2 text-sm disabled:opacity-50", children: t('pages.focus.workbench.confirm') }), _jsx("button", { type: "button", onClick: () => setMode(null), className: "ml-3 text-sm text-ink-3", children: t('pages.focus.workbench.cancel') })] })] }), showPrincipleLink && _jsx(Link, { className: "mt-5 inline-block text-[13px] text-gov underline underline-offset-4", state: { returnTo: `/focus${location.search}` }, to: `/principles/${encodeURIComponent(principleId)}`, children: t('pages.focus.workbench.openPrinciple') }), _jsxs("details", { className: "mt-4 text-[12px] text-ink-4", children: [_jsx("summary", { className: "cursor-pointer", children: t('pages.focus.workbench.subjectDetails') }), _jsxs("dl", { className: "mt-2 break-all font-mono", children: [_jsxs("div", { children: ["approval: ", approvalId] }), _jsxs("div", { children: ["artifact: ", review?.expectedReview.artifactId ?? artifactId] }), _jsxs("div", { children: ["channel: ", review?.expectedReview.channel ?? channel] })] })] })] })] });
133
+ }
@@ -64,24 +64,16 @@ export declare function partitionInboxForDisplay<T extends {
64
64
  };
65
65
  /**
66
66
  * SPEC-PD-UIUX-001 R2 (Owner review round 2): the conclusion strip's headline
67
- * and total must agree with the objects actually on screen and with the inbox
68
- * response contract.
67
+ * must never claim the system is clear when the inbox response cannot back
68
+ * that claim.
69
69
  *
70
70
  * Modes (display-only; no permission derivation):
71
71
  * - 'unavailable' — inbox projection off (feature_disabled surfaces as
72
- * ownerInbox===null with NO error) or the request failed: the pending count
73
- * CANNOT be confirmed; never allClear.
72
+ * ownerInbox===null with NO error) or the request failed.
74
73
  * - 'degraded' — the inbox answered HTTP-success but carries
75
74
  * `degraded: { reason, nextAction }` (e.g. principle ledger unreadable, the
76
- * server then returns EMPTY groups): counts cannot be trusted; the strip
77
- * shows the server's reason + nextAction and never allClear.
78
- * - 'ok' — counts are trusted.
79
- *
80
- * Counting (S2): displayTotal = actionableCount + inbox pending items NOT
81
- * already represented by an on-screen action card. The association is the
82
- * verified principleId field (partition.unmatchedDecisions are inbox items
83
- * WITHOUT a card). One inbox item matching N cards of the same principle
84
- * counts the N cards (each is a distinct decision); disjoint sets sum.
75
+ * server then returns EMPTY groups): the strip shows the server's reason.
76
+ * - 'ok' — the inbox answered without degradation.
85
77
  */
86
78
  export declare function deriveConclusionDisplay(input: {
87
79
  ownerInbox: {
@@ -94,26 +86,13 @@ export declare function deriveConclusionDisplay(input: {
94
86
  };
95
87
  } | null;
96
88
  ownerInboxError: string | null;
97
- partition: {
98
- pendingTotal: number;
99
- unmatchedDecisions: unknown[];
100
- } | null;
101
- actionableCount: number;
102
89
  }): {
103
90
  mode: 'ok' | 'degraded' | 'unavailable';
104
- displayTotal: number;
105
- actionableCount: number;
106
91
  degraded?: {
107
92
  reason: string;
108
93
  nextAction: string;
109
94
  };
110
95
  };
111
- /**
112
- * SPEC-PD-UIUX-001 R2 (Owner review S3): EVERY conclusion branch must express
113
- * governance readiness the same way — "actionable now" is only claimable when
114
- * the actions are not locked. One matrix, one source.
115
- */
116
- export declare function resolveConclusionActionableKey(mode: 'ok' | 'degraded' | 'unavailable', locked: boolean): string;
117
96
  export interface OwnerConfigureCommand {
118
97
  /** i18n key for the shell label, e.g. "pages.focus.experience.ownerGuide.cmdPowerShell". */
119
98
  labelKey: string;