create-principles-disciple 1.145.20 → 1.145.22

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 (148) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/console/dist/server/index.js +11 -0
  3. package/console/dist/server/models/ApprovalsConsoleModel.js +10 -11
  4. package/console/dist/server/models/RuleCandidateBindingConsoleModel.d.ts +40 -0
  5. package/console/dist/server/models/RuleCandidateBindingConsoleModel.js +130 -0
  6. package/console/dist/server/models/artifact-principle-resolver.d.ts +2 -2
  7. package/console/dist/server/models/artifact-principle-resolver.js +10 -1
  8. package/console/dist/server/models/principle-id-resolution.d.ts +7 -12
  9. package/console/dist/server/models/principle-id-resolution.js +7 -94
  10. package/console/dist/server/routes/rule-candidate-bindings.d.ts +10 -0
  11. package/console/dist/server/routes/rule-candidate-bindings.js +116 -0
  12. package/console/dist/shared/rule-candidate-binding-contract.d.ts +30 -0
  13. package/console/dist/shared/rule-candidate-binding-contract.js +1 -0
  14. package/console/dist/ui/api.d.ts +9 -0
  15. package/console/dist/ui/api.js +10 -0
  16. package/console/dist/ui/i18n/en.json +25 -0
  17. package/console/dist/ui/i18n/zh-CN.json +25 -0
  18. package/console/dist/ui/pages/focus/FocusPage.js +2 -1
  19. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +2 -0
  20. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +68 -0
  21. package/console/dist/ui/utils/rule-binding-validators.d.ts +8 -0
  22. package/console/dist/ui/utils/rule-binding-validators.js +46 -0
  23. package/console/dist/web/assets/app.css +6 -0
  24. package/console/dist/web/assets/app.js +30 -30
  25. package/core/dist/principle-tree-ledger.d.ts +24 -4
  26. package/core/dist/principle-tree-ledger.d.ts.map +1 -1
  27. package/core/dist/principle-tree-ledger.js +201 -24
  28. package/core/dist/principle-tree-ledger.js.map +1 -1
  29. package/core/dist/runtime-v2/activation/ledger-identity.d.ts +4 -22
  30. package/core/dist/runtime-v2/activation/ledger-identity.d.ts.map +1 -1
  31. package/core/dist/runtime-v2/activation/ledger-identity.js +54 -0
  32. package/core/dist/runtime-v2/activation/ledger-identity.js.map +1 -1
  33. package/core/dist/runtime-v2/adapter/artificer-l2-adapter.d.ts.map +1 -1
  34. package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js +18 -0
  35. package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js.map +1 -1
  36. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts +11 -0
  37. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts.map +1 -1
  38. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js +67 -1
  39. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js.map +1 -1
  40. package/core/dist/runtime-v2/index.d.ts +2 -0
  41. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  42. package/core/dist/runtime-v2/index.js +1 -0
  43. package/core/dist/runtime-v2/index.js.map +1 -1
  44. package/core/dist/runtime-v2/internalization/artificer-intent-preflight.d.ts.map +1 -1
  45. package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js +10 -0
  46. package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js.map +1 -1
  47. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
  48. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +3 -1
  49. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
  50. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts +3 -0
  51. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
  52. package/core/dist/runtime-v2/internalization/artificer-runner.js +23 -3
  53. package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
  54. package/core/dist/runtime-v2/internalization/candidate-identity.d.ts +17 -0
  55. package/core/dist/runtime-v2/internalization/candidate-identity.d.ts.map +1 -0
  56. package/core/dist/runtime-v2/internalization/candidate-identity.js +56 -0
  57. package/core/dist/runtime-v2/internalization/candidate-identity.js.map +1 -0
  58. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +2 -0
  59. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  60. package/core/dist/runtime-v2/internalization/evaluator-output.js +3 -0
  61. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  62. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +2 -2
  63. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
  64. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +90 -38
  65. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
  66. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +2 -0
  67. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  68. package/core/dist/runtime-v2/internalization/evaluator-runner.js +158 -41
  69. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  70. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +4 -0
  71. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
  72. package/core/dist/runtime-v2/internalization/peer-runner-contracts.js +13 -0
  73. package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
  74. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +2 -0
  75. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
  76. package/core/dist/runtime-v2/internalization/pitask-metadata.js +8 -0
  77. package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
  78. package/core/dist/runtime-v2/internalization/rule-binding-revision.d.ts +28 -0
  79. package/core/dist/runtime-v2/internalization/rule-binding-revision.d.ts.map +1 -0
  80. package/core/dist/runtime-v2/internalization/rule-binding-revision.js +92 -0
  81. package/core/dist/runtime-v2/internalization/rule-binding-revision.js.map +1 -0
  82. package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.d.ts +66 -0
  83. package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.d.ts.map +1 -0
  84. package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js +150 -0
  85. package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js.map +1 -0
  86. package/core/dist/runtime-v2/internalization/scribe-runner.d.ts +7 -1
  87. package/core/dist/runtime-v2/internalization/scribe-runner.d.ts.map +1 -1
  88. package/core/dist/runtime-v2/internalization/scribe-runner.js +67 -3
  89. package/core/dist/runtime-v2/internalization/scribe-runner.js.map +1 -1
  90. package/core/dist/runtime-v2/principle-tree/ledger-codec.d.ts.map +1 -1
  91. package/core/dist/runtime-v2/principle-tree/ledger-codec.js +3 -6
  92. package/core/dist/runtime-v2/principle-tree/ledger-codec.js.map +1 -1
  93. package/core/dist/runtime-v2/principle-tree/rule-candidate-binding.d.ts +12 -0
  94. package/core/dist/runtime-v2/principle-tree/rule-candidate-binding.d.ts.map +1 -0
  95. package/core/dist/runtime-v2/principle-tree/rule-candidate-binding.js +129 -0
  96. package/core/dist/runtime-v2/principle-tree/rule-candidate-binding.js.map +1 -0
  97. package/core/dist/runtime-v2/store/artifact/sqlite-pi-artifact-store.d.ts.map +1 -1
  98. package/core/dist/runtime-v2/store/artifact/sqlite-pi-artifact-store.js +70 -6
  99. package/core/dist/runtime-v2/store/artifact/sqlite-pi-artifact-store.js.map +1 -1
  100. package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
  101. package/core/dist/runtime-v2/store/sqlite-connection.js +15 -1
  102. package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
  103. package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.d.ts +10 -0
  104. package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.d.ts.map +1 -1
  105. package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js +55 -5
  106. package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js.map +1 -1
  107. package/core/dist/runtime-v2/types/ledger-store.d.ts +3 -0
  108. package/core/dist/runtime-v2/types/ledger-store.d.ts.map +1 -1
  109. package/core/dist/runtime-v2/types/ledger-store.js.map +1 -1
  110. package/core/dist/runtime-v2/types/rule-candidate-binding.d.ts +98 -0
  111. package/core/dist/runtime-v2/types/rule-candidate-binding.d.ts.map +1 -0
  112. package/core/dist/runtime-v2/types/rule-candidate-binding.js +2 -0
  113. package/core/dist/runtime-v2/types/rule-candidate-binding.js.map +1 -0
  114. package/core/dist/telemetry-event.d.ts +2 -2
  115. package/core/dist/telemetry-event.d.ts.map +1 -1
  116. package/core/dist/telemetry-event.js +1 -0
  117. package/core/dist/telemetry-event.js.map +1 -1
  118. package/core/package.json +1 -1
  119. package/host-runtime/dist/internalization-consumer-cycle.js +12 -0
  120. package/host-runtime/dist/internalization-consumer-governance.js +4 -0
  121. package/host-runtime/package.json +1 -1
  122. package/package.json +1 -1
  123. package/pd-cli/dist/commands/codex-worker.d.ts.map +1 -1
  124. package/pd-cli/dist/commands/codex-worker.js +6 -11
  125. package/pd-cli/dist/commands/codex-worker.js.map +1 -1
  126. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  127. package/pd-cli/dist/commands/runtime-activation.js +4 -0
  128. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  129. package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
  130. package/pd-cli/dist/commands/runtime-internalization-run-once.js +10 -1
  131. package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
  132. package/pd-cli/dist/commands/workbuddy-worker.d.ts.map +1 -1
  133. package/pd-cli/dist/commands/workbuddy-worker.js +6 -11
  134. package/pd-cli/dist/commands/workbuddy-worker.js.map +1 -1
  135. package/pd-cli/dist/commands/worker-drain.d.ts +7 -0
  136. package/pd-cli/dist/commands/worker-drain.d.ts.map +1 -0
  137. package/pd-cli/dist/commands/worker-drain.js +51 -0
  138. package/pd-cli/dist/commands/worker-drain.js.map +1 -0
  139. package/pd-cli/dist/services/demo-story-a-runner.d.ts.map +1 -1
  140. package/pd-cli/dist/services/demo-story-a-runner.js +43 -2
  141. package/pd-cli/dist/services/demo-story-a-runner.js.map +1 -1
  142. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts +8 -30
  143. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
  144. package/pd-cli/dist/services/rulehost-pipeline-runner.js +76 -124
  145. package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
  146. package/pd-cli/package.json +1 -1
  147. package/plugin/dist/bundle.js +616 -612
  148. package/release-manager/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { clearStoredToken, loadStoredToken, storeToken } from "./utils/token-storage.js";
2
+ import { validateBindingReview, validateBindingQueue, validateBindingMutation } from './utils/rule-binding-validators.js';
2
3
  import { validateErrorResponse, validateHeaders, validateFeedbackReport, validateFeedbackDraftsList, validateFeedbackDraftEnvelope, validateFeedbackChannels, validateFeedbackSubmitResult, validateDeleteEnvelope, validateWorkspaceEntry, validateWorkspaceList, validateRemovedEnvelope, validateSyncResult, validateConfigSummary, validateConfigCatalog, validateAgentBindingUpdate, validateDefaultRuntimeUpdate, validateRuntimeProfileMutation, validateReadinessCheck, validateFeatureFlagUpdate, validateOutputLanguage, validateGovernanceQueue, validateRecoveryResult, validateFailedTaskDetail, validateOwnerDecisionsData, validateOwnerResolutionResult, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateUpdateRecovery, validateUpdateTransaction, validateApprovalRecordDirect, validatePrinciplesList, validateCorePrinciples, validateApprovalsGrouped, validateEvidenceChain, validateTrajectoryData, validateIntentSummary, validateIntentDecisionList, validateIntentDecisionResult, validateIntentDecisionSummary, validateFollowUpResponse, validateIntentInitResult, validateIntentSaveResult, validateIntentRawContent, validateIntentVersions, validateOwnerGovernanceView, validateOwnerDecisionViewCore, validateOwnerDecisionInbox, validateOwnerDecisionLibrary, validateGovernanceExperienceSnapshot, validateRuleCodeOwnerReview, validateRuleCodeMutation, validateOwnerIdentityView, validateOwnerIdentityRegister, validateOwnerIdentityUnregister, } from "./utils/validators.js";
3
4
  // ── Auth ──────────────────────────────────────────────────────────────────────
4
5
  function getToken() {
@@ -126,6 +127,15 @@ async function checkAuth() {
126
127
  return result.success;
127
128
  }
128
129
  // ── Workspaces ────────────────────────────────────────────────────────────────
130
+ export function fetchRuleBindingQueue() {
131
+ return request('/api/v1/governance/rule-candidate-bindings', undefined, validateBindingQueue);
132
+ }
133
+ export function fetchRuleBindingReview(candidateId, search = '') {
134
+ return request(`/api/v1/governance/rule-candidate-bindings/${encodeURIComponent(candidateId)}/review?search=${encodeURIComponent(search)}`, undefined, validateBindingReview);
135
+ }
136
+ export function mutateRuleBinding(candidateId, body) {
137
+ return request(`/api/v1/governance/rule-candidate-bindings/${encodeURIComponent(candidateId)}`, { method: 'POST', body: JSON.stringify(body) }, validateBindingMutation);
138
+ }
129
139
  async function fetchWorkspaces() {
130
140
  return request("/api/workspaces", undefined, validateWorkspaceList);
131
141
  }
@@ -48,6 +48,31 @@
48
48
  },
49
49
  "pages": {
50
50
  "focus": {
51
+ "ruleBinding": {
52
+ "title": "Formal principle for a rule",
53
+ "separate": "A binding identifies the principle implemented by this rule. It does not approve or activate the rule, or approve a development design.",
54
+ "unavailable": "Binding sources are unavailable. Reload to retry.",
55
+ "awaiting": "Identity needs review",
56
+ "manage": "Manage binding",
57
+ "candidate": "Original rule candidate",
58
+ "pain": "Source pain",
59
+ "evidence": "Diagnosis evidence",
60
+ "current": "Current binding",
61
+ "unlinked": "Unlinked",
62
+ "search": "Search formal principles",
63
+ "noTarget": "Keep this unlinked if no formal principle is suitable.",
64
+ "formation": "View the existing principle workflow",
65
+ "version": "Version",
66
+ "declaration": "Submitting declares this rule an implementation of the selected principle in the stated scope, and authorizes one bounded revision, generation and evaluation using the current configuration.",
67
+ "reason": "Reason for binding or revocation",
68
+ "bind": "Bind and resume once",
69
+ "revoke": "Revoke binding",
70
+ "bound": "Binding saved and one bounded revision requested. Rule approval remains separate.",
71
+ "resumeFailed": "Binding saved but revision has not started. Inspect the recovery reason. Prior artifacts are unchanged; no automatic retry will occur.",
72
+ "revoked": "Binding revoked. Historical artifacts remain. Running rules require separate deactivation.",
73
+ "revokeBoundary": "Revocation does not stop running rules. In-flight operations that passed their final check may complete.",
74
+ "stop": "View running rules and deactivation"
75
+ },
51
76
  "eyebrow": "Governance Focus",
52
77
  "title": "What deserves your judgment right now",
53
78
  "subtitle": "This page answers one question: what should the owner review next?",
@@ -48,6 +48,31 @@
48
48
  },
49
49
  "pages": {
50
50
  "focus": {
51
+ "ruleBinding": {
52
+ "title": "规则的正式原则归属",
53
+ "separate": "关联仅声明规则属于哪条原则;不批准规则、不启用规则,也不代替开发设计批准。",
54
+ "unavailable": "关联材料暂不可读,请重新加载。",
55
+ "awaiting": "待核实归属",
56
+ "manage": "管理已有归属",
57
+ "candidate": "规则候选原文",
58
+ "pain": "来源痛苦信号",
59
+ "evidence": "诊断依据",
60
+ "current": "当前关联",
61
+ "unlinked": "尚未关联",
62
+ "search": "搜索正式原则",
63
+ "noTarget": "没有合适的正式原则时,保持未关联。",
64
+ "formation": "查看既有原则形成流程",
65
+ "version": "版本",
66
+ "declaration": "提交表示:该规则是所选原则在上述范围内的一种实现,并授权按现有配置自动续跑一次修订、生成和验证。",
67
+ "reason": "关联或撤回的理由",
68
+ "bind": "关联并自动续跑一次",
69
+ "revoke": "撤回归属",
70
+ "bound": "关联已保存,已请求一次受控修订;后续规则仍须单独审批。",
71
+ "resumeFailed": "关联已保存,但修订尚未开始。请查看恢复原因;旧工件未变,不会自动重试。",
72
+ "revoked": "归属已撤回;历史工件保留。运行中的规则须另行停用。",
73
+ "revokeBoundary": "撤回不停止已有运行规则;撤回前已通过最终检查的在途操作仍可能完成。",
74
+ "stop": "查看规则运行与停用入口"
75
+ },
51
76
  "eyebrow": "治理焦点",
52
77
  "title": "现在,值得你判断的事",
53
78
  "subtitle": "这一页只回答一个问题:现在该做什么判断。",
@@ -12,6 +12,7 @@ import { fetchGovernanceQueue, fetchApprovalsGrouped, approveApproval, rejectApp
12
12
  import { selectApprovalGroupDisplayTitle, showsUnlinkedCandidateNote, validateApprovalsGroupedData } from "./focus-validation.js";
13
13
  import { splitReadingTitleBody } from "../../utils/reading-layout.js";
14
14
  import { OwnerDecisionCard } from "./OwnerDecisionCard.js";
15
+ import { RuleCandidateBindingPanel } from './RuleCandidateBindingPanel.js';
15
16
  import { localizeApprovalWarning, splitApprovalWarnings } from "../../utils/approval-warning-localization.js";
16
17
  /** PRI-908: how long the decided card stays visible before the pending list refresh removes it. */
17
18
  const DECIDED_REFRESH_DELAY_MS = 1600;
@@ -806,7 +807,7 @@ export function FocusPage({ featureFlags }) {
806
807
  ? "bg-green/10 text-green border border-green/20"
807
808
  : "text-ink-4 border border-line bg-surface/80"}`, role: "status", "data-testid": "focus-conclusion-badge", children: experienceMode && experienceData !== null
808
809
  ? t(EXPERIENCE_ATTENTION[experienceData.summary.primaryAttention].labelKey)
809
- : t(`pages.focus.stateLabel.${governanceState}`) }), _jsx("div", { className: "text-ink-2 text-[13px] leading-relaxed", "data-testid": "focus-conclusion-counts", children: conclusionDisplay.mode === 'ok' && conclusionDisplay.displayTotal === 0 ? (_jsx("span", { children: t("pages.focus.conclusion.allClear") })) : conclusionDisplay.mode === 'unavailable' || conclusionDisplay.mode === 'degraded' ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium", "data-testid": "focus-conclusion-uncertain", children: t("pages.focus.conclusion.pendingUncertain") }), conclusionDisplay.actionableCount > 0 && (_jsxs("span", { className: "text-ink-4", children: [" · ", t(resolveConclusionActionableKey(conclusionDisplay.mode, ownerActionsLockedReason !== null), { count: conclusionDisplay.actionableCount })] }))] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium", children: t("pages.focus.conclusion.pendingTotal", { count: conclusionDisplay.displayTotal }) }), _jsxs("span", { className: "text-ink-4", children: [" · ", t(resolveConclusionActionableKey(conclusionDisplay.mode, ownerActionsLockedReason !== null), { count: conclusionDisplay.actionableCount })] })] })) })] }), conclusionDisplay.mode === 'degraded' && conclusionDisplay.degraded && (_jsxs("div", { className: "mt-3 border border-amber/30 rounded-[6px] bg-surface px-3 py-2.5", "data-testid": "focus-conclusion-degraded", children: [_jsxs("p", { className: "text-[12px] text-ink-3 leading-relaxed", children: [t("pages.focus.conclusion.degradedLabel"), " ", conclusionDisplay.degraded.reason] }), _jsx("p", { className: "mt-1 text-[12px] text-ink-3 leading-relaxed", children: conclusionDisplay.degraded.nextAction })] })), ownerActionsLockedReason !== null && (_jsxs("div", { id: "focus-global-lock-notice", className: "mt-3 flex items-start justify-between gap-3 border border-amber/30 rounded-[6px] bg-surface px-3 py-2.5", "data-testid": "focus-conclusion-lock", children: [_jsx("p", { className: "text-[12px] text-ink-3 leading-relaxed", children: ownerActionsLockedReason }), _jsx(Link, { to: "/settings", className: "shrink-0 text-[12px] text-gov underline hover:text-gov-2", children: t("pages.focus.ownerDecision.goSettingsCta") })] })), _jsx("p", { className: "mt-2 text-ink-4 text-[11.5px] leading-relaxed", "data-testid": "focus-conclusion-scope-note", children: t("pages.focus.conclusion.countsNote") })] }), _jsxs("section", { "aria-label": t("pages.focus.ownerDecision.sectionTitle"), className: "mb-7", "data-testid": "owner-decisions-section", children: [_jsxs("div", { className: "flex items-baseline gap-2 mb-3", children: [_jsx(SectionTitle, { children: t("pages.focus.ownerDecision.sectionTitle") }), _jsx("span", { className: "font-mono text-ink-4 text-[12px]", children: ownerDecisionError !== null
810
+ : t(`pages.focus.stateLabel.${governanceState}`) }), _jsx("div", { className: "text-ink-2 text-[13px] leading-relaxed", "data-testid": "focus-conclusion-counts", children: conclusionDisplay.mode === 'ok' && conclusionDisplay.displayTotal === 0 ? (_jsx("span", { children: t("pages.focus.conclusion.allClear") })) : conclusionDisplay.mode === 'unavailable' || conclusionDisplay.mode === 'degraded' ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium", "data-testid": "focus-conclusion-uncertain", children: t("pages.focus.conclusion.pendingUncertain") }), conclusionDisplay.actionableCount > 0 && (_jsxs("span", { className: "text-ink-4", children: [" · ", t(resolveConclusionActionableKey(conclusionDisplay.mode, ownerActionsLockedReason !== null), { count: conclusionDisplay.actionableCount })] }))] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium", children: t("pages.focus.conclusion.pendingTotal", { count: conclusionDisplay.displayTotal }) }), _jsxs("span", { className: "text-ink-4", children: [" · ", t(resolveConclusionActionableKey(conclusionDisplay.mode, ownerActionsLockedReason !== null), { count: conclusionDisplay.actionableCount })] })] })) })] }), conclusionDisplay.mode === 'degraded' && conclusionDisplay.degraded && (_jsxs("div", { className: "mt-3 border border-amber/30 rounded-[6px] bg-surface px-3 py-2.5", "data-testid": "focus-conclusion-degraded", children: [_jsxs("p", { className: "text-[12px] text-ink-3 leading-relaxed", children: [t("pages.focus.conclusion.degradedLabel"), " ", conclusionDisplay.degraded.reason] }), _jsx("p", { className: "mt-1 text-[12px] text-ink-3 leading-relaxed", children: conclusionDisplay.degraded.nextAction })] })), ownerActionsLockedReason !== null && (_jsxs("div", { id: "focus-global-lock-notice", className: "mt-3 flex items-start justify-between gap-3 border border-amber/30 rounded-[6px] bg-surface px-3 py-2.5", "data-testid": "focus-conclusion-lock", children: [_jsx("p", { className: "text-[12px] text-ink-3 leading-relaxed", children: ownerActionsLockedReason }), _jsx(Link, { to: "/settings", className: "shrink-0 text-[12px] text-gov underline hover:text-gov-2", children: t("pages.focus.ownerDecision.goSettingsCta") })] })), _jsx("p", { className: "mt-2 text-ink-4 text-[11.5px] leading-relaxed", "data-testid": "focus-conclusion-scope-note", children: t("pages.focus.conclusion.countsNote") })] }), _jsx(RuleCandidateBindingPanel, {}), _jsxs("section", { "aria-label": t("pages.focus.ownerDecision.sectionTitle"), className: "mb-7", "data-testid": "owner-decisions-section", children: [_jsxs("div", { className: "flex items-baseline gap-2 mb-3", children: [_jsx(SectionTitle, { children: t("pages.focus.ownerDecision.sectionTitle") }), _jsx("span", { className: "font-mono text-ink-4 text-[12px]", children: ownerDecisionError !== null
810
811
  ? t("pages.focus.ownerDecision.unavailable")
811
812
  : `· ${decisionItems.length + (showApprovalCards ? pendingGroups.length : 0)}` })] }), ownerDecisionError !== null && (_jsx("p", { className: "text-ink-4 text-[12.5px]", children: t("pages.focus.ownerDecision.loadError") })), ownerDecisionError === null && decisionItems.length === 0 && !showApprovalCards && (_jsx("p", { className: "text-ink-4 text-[13px]", children: t("pages.focus.ownerDecision.empty") })), decisionItems.map((item) => (_jsx(OwnerDecisionCard, { item: item, governanceReady: experienceData === null || (experienceData.readiness.authenticationMode === "authenticated"
812
813
  && experienceData.readiness.ownerIdentityConfiguration === "configured"), actionsLockedReason: ownerActionsLockedReason ?? undefined, onResolved: () => { void loadData(); } }, item.reviewKey))), (() => {
@@ -0,0 +1,2 @@
1
+ /** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
2
+ export declare function RuleCandidateBindingPanel(): import("react").JSX.Element | null;
@@ -0,0 +1,68 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Link } from 'react-router-dom';
5
+ import { fetchRuleBindingQueue, fetchRuleBindingReview, mutateRuleBinding } from '../../api.js';
6
+ /** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
7
+ export function RuleCandidateBindingPanel() {
8
+ const { t } = useTranslation();
9
+ const [queue, setQueue] = useState(null);
10
+ const [review, setReview] = useState(null);
11
+ const [search, setSearch] = useState('');
12
+ const [selectedId, setSelectedId] = useState('');
13
+ const [reason, setReason] = useState('');
14
+ const [busy, setBusy] = useState(false);
15
+ const [message, setMessage] = useState('');
16
+ const [error, setError] = useState('');
17
+ useEffect(() => {
18
+ let active = true;
19
+ void fetchRuleBindingQueue().then(result => {
20
+ if (!active)
21
+ return;
22
+ if (result.success)
23
+ setQueue(result.data);
24
+ else
25
+ setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
26
+ });
27
+ return () => { active = false; };
28
+ }, [t]);
29
+ async function load(candidateId, query = '') {
30
+ setBusy(true);
31
+ setError('');
32
+ const result = await fetchRuleBindingReview(candidateId, query);
33
+ if (result.success) {
34
+ setReview(result.data);
35
+ setSelectedId('');
36
+ }
37
+ else
38
+ setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
39
+ setBusy(false);
40
+ }
41
+ async function decide(action) {
42
+ if (!review)
43
+ return;
44
+ const target = review.eligibleTargets.find(p => p.principleId === selectedId);
45
+ if (action === 'bind' && !target)
46
+ return;
47
+ setBusy(true);
48
+ setError('');
49
+ setMessage('');
50
+ const requestId = crypto.randomUUID();
51
+ const common = { expectedRevision: review.currentRevision, reason, requestId };
52
+ const result = await mutateRuleBinding(review.candidateId, action === 'bind' && target
53
+ ? { ...common, action: 'bind', resumeOnce: true, principleId: target.principleId, expectedCandidateDigest: review.candidateDigest, expectedPrincipleDigest: target.digest }
54
+ : { ...common, action: 'revoke', resumeOnce: false });
55
+ if (result.success) {
56
+ setMessage(action === 'revoke' ? t('pages.focus.ruleBinding.revoked')
57
+ : result.data.revisionStatus === 'refused' ? t('pages.focus.ruleBinding.resumeFailed') : t('pages.focus.ruleBinding.bound'));
58
+ await load(review.candidateId, search);
59
+ }
60
+ else
61
+ setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
62
+ setBusy(false);
63
+ }
64
+ if (queue?.items.length === 0 && !error)
65
+ return null;
66
+ 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') }), error && _jsx("p", { role: "alert", className: "my-2 text-amber", children: error }), message && _jsx("p", { role: "status", className: "my-2", children: message }), queue?.items.map(item => _jsxs("button", { className: "mt-2 block text-left text-sm underline", disabled: busy, onClick: () => { setSearch(''); setReason(''); void load(item.candidateId); }, children: [item.reason === 'bound' ? t('pages.focus.ruleBinding.manage') : t('pages.focus.ruleBinding.awaiting'), " \u00B7 ", item.text.slice(0, 100)] }, item.candidateId)), review && _jsxs("div", { className: "mt-4 border-t border-line pt-3", children: [_jsx("h3", { className: "font-medium", children: t('pages.focus.ruleBinding.candidate') }), _jsx("p", { className: "whitespace-pre-wrap text-sm", children: review.candidateText }), _jsxs("p", { className: "mt-2 text-sm", children: [t('pages.focus.ruleBinding.pain'), "\uFF1A", review.sourcePainId] }), _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 })] }), _jsxs("p", { className: "mt-3 text-sm", children: [t('pages.focus.ruleBinding.current'), "\uFF1A", bound ? review.currentBinding?.principleId : t('pages.focus.ruleBinding.unlinked')] }), !bound && _jsxs(_Fragment, { children: [_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) })] }), _jsx("button", { className: "mt-3 rounded border border-gov px-3 py-2 text-sm", disabled: busy || !reason.trim() || (!bound && !selectedId), onClick: () => void decide(bound ? 'revoke' : 'bind'), children: bound ? t('pages.focus.ruleBinding.revoke') : t('pages.focus.ruleBinding.bind') }), _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') })] })] })] });
68
+ }
@@ -0,0 +1,8 @@
1
+ import type { RuleBindingReviewView, RuleBindingQueueView } from '../../shared/rule-candidate-binding-contract.js';
2
+ export declare function validateBindingQueue(value: unknown): RuleBindingQueueView | null;
3
+ export declare function validateBindingReview(value: unknown): RuleBindingReviewView | null;
4
+ export declare function validateBindingMutation(value: unknown): {
5
+ status: string;
6
+ revisionStatus?: string;
7
+ nextAction?: string;
8
+ } | null;
@@ -0,0 +1,46 @@
1
+ function record(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); }
2
+ function integer(value) { return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0; }
3
+ export function validateBindingQueue(value) {
4
+ if (!record(value) || !Array.isArray(value.items))
5
+ return null;
6
+ const items = [];
7
+ for (const item of value.items) {
8
+ if (!record(item) || typeof item.candidateId !== 'string' || typeof item.text !== 'string' || typeof item.reason !== 'string')
9
+ return null;
10
+ items.push({ candidateId: item.candidateId, text: item.text, reason: item.reason });
11
+ }
12
+ return { items };
13
+ }
14
+ export function validateBindingReview(value) {
15
+ if (!record(value) || typeof value.candidateId !== 'string' || typeof value.candidateText !== 'string' || typeof value.sourcePainId !== 'string'
16
+ || typeof value.evidence !== 'string' || typeof value.candidateDigest !== 'string' || !integer(value.currentRevision)
17
+ || typeof value.resumeNotice !== 'string' || !Array.isArray(value.eligibleTargets) || value.eligibleTargets.length > 20)
18
+ return null;
19
+ let currentBinding = null;
20
+ if (value.currentBinding !== null) {
21
+ const b = value.currentBinding;
22
+ if (!record(b) || (b.action !== 'bind' && b.action !== 'revoke') || typeof b.principleId !== 'string' || typeof b.reason !== 'string' || !integer(b.revision))
23
+ return null;
24
+ currentBinding = { action: b.action, principleId: b.principleId, reason: b.reason, revision: b.revision };
25
+ }
26
+ const eligibleTargets = [];
27
+ for (const p of value.eligibleTargets) {
28
+ if (!record(p) || typeof p.principleId !== 'string' || typeof p.text !== 'string' || typeof p.status !== 'string' || !integer(p.version) || typeof p.digest !== 'string')
29
+ return null;
30
+ eligibleTargets.push({ principleId: p.principleId, text: p.text, status: p.status, version: p.version, digest: p.digest });
31
+ }
32
+ return { candidateId: value.candidateId, candidateText: value.candidateText, sourcePainId: value.sourcePainId, evidence: value.evidence,
33
+ candidateDigest: value.candidateDigest, currentRevision: value.currentRevision, currentBinding, eligibleTargets, resumeNotice: value.resumeNotice };
34
+ }
35
+ export function validateBindingMutation(value) {
36
+ if (!record(value) || !['bound', 'already_bound', 'revoked', 'already_revoked'].includes(String(value.status)) || typeof value.status !== 'string')
37
+ return null;
38
+ if (value.runtimeDeactivated !== false || value.inFlightOperationsMayComplete !== true)
39
+ return null;
40
+ if (value.nextAction !== undefined && typeof value.nextAction !== 'string')
41
+ return null;
42
+ if (value.revision !== undefined && (!record(value.revision) || typeof value.revision.status !== 'string'))
43
+ return null;
44
+ return { status: value.status, ...(record(value.revision) && typeof value.revision.status === 'string' ? { revisionStatus: value.revision.status } : {}),
45
+ ...(typeof value.nextAction === 'string' ? { nextAction: value.nextAction } : {}) };
46
+ }
@@ -398,6 +398,12 @@
398
398
  .my-1 {
399
399
  margin-block: var(--spacing);
400
400
  }
401
+ .my-2 {
402
+ margin-block: calc(var(--spacing) * 2);
403
+ }
404
+ .my-3 {
405
+ margin-block: calc(var(--spacing) * 3);
406
+ }
401
407
  .my-5 {
402
408
  margin-block: calc(var(--spacing) * 5);
403
409
  }