create-principles-disciple 1.121.10 → 1.122.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/console/dist/server/index.js +17 -0
  2. package/console/dist/server/models/OwnerDecisionConsoleModel.d.ts +39 -0
  3. package/console/dist/server/models/OwnerDecisionConsoleModel.js +233 -0
  4. package/console/dist/server/models/PrincipleClassifier.d.ts +3 -3
  5. package/console/dist/server/models/PrincipleClassifier.js +12 -5
  6. package/console/dist/server/models/PrinciplesConsoleModel.d.ts +1 -1
  7. package/console/dist/server/models/PrinciplesConsoleModel.js +2 -2
  8. package/console/dist/server/routes/failed-tasks.js +40 -2
  9. package/console/dist/server/routes/owner-decisions.d.ts +25 -0
  10. package/console/dist/server/routes/owner-decisions.js +186 -0
  11. package/console/dist/server/routes/principles.js +15 -7
  12. package/console/dist/ui/api.d.ts +12 -1
  13. package/console/dist/ui/api.js +9 -2
  14. package/console/dist/ui/components/notifications/NotificationProvider.js +8 -2
  15. package/console/dist/ui/i18n/en.json +33 -1
  16. package/console/dist/ui/i18n/zh-CN.json +33 -1
  17. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +5 -1
  18. package/console/dist/ui/pages/focus/FocusPage.js +19 -2
  19. package/console/dist/ui/pages/focus/OwnerDecisionCard.d.ts +6 -0
  20. package/console/dist/ui/pages/focus/OwnerDecisionCard.js +86 -0
  21. package/console/dist/ui/pages/principles/PrinciplesPage.js +11 -5
  22. package/console/dist/ui/utils/validators.d.ts +37 -0
  23. package/console/dist/ui/utils/validators.js +123 -0
  24. package/console/dist/web/assets/app.css +11 -0
  25. package/console/dist/web/assets/app.js +2740 -2350
  26. package/core/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js +20 -0
  27. package/core/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js.map +1 -1
  28. package/core/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js +26 -5
  29. package/core/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js.map +1 -1
  30. package/core/dist/runtime-v2/index.d.ts +7 -1
  31. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  32. package/core/dist/runtime-v2/index.js +4 -0
  33. package/core/dist/runtime-v2/index.js.map +1 -1
  34. package/core/dist/runtime-v2/internalization/__tests__/evaluator-prompt-builder.test.js +1 -1
  35. package/core/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js +15 -14
  36. package/core/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js.map +1 -1
  37. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts +2 -0
  38. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts.map +1 -0
  39. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js +56 -0
  40. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js.map +1 -0
  41. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts +2 -0
  42. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts.map +1 -0
  43. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +883 -0
  44. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -0
  45. package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts +2 -0
  46. package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts.map +1 -0
  47. package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js +328 -0
  48. package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js.map +1 -0
  49. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +63 -2
  50. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  51. package/core/dist/runtime-v2/internalization/evaluator-output.js +139 -2
  52. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  53. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +54 -2
  54. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
  55. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +49 -2
  56. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
  57. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +34 -0
  58. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  59. package/core/dist/runtime-v2/internalization/evaluator-runner.js +295 -12
  60. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  61. package/core/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
  62. package/core/dist/runtime-v2/internalization/internalization-orchestrator.js +10 -54
  63. package/core/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
  64. package/core/dist/runtime-v2/internalization/internalization-transition-decision.d.ts +7 -1
  65. package/core/dist/runtime-v2/internalization/internalization-transition-decision.d.ts.map +1 -1
  66. package/core/dist/runtime-v2/internalization/internalization-transition-decision.js +9 -2
  67. package/core/dist/runtime-v2/internalization/internalization-transition-decision.js.map +1 -1
  68. package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts +89 -0
  69. package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts.map +1 -0
  70. package/core/dist/runtime-v2/internalization/owner-resolution-service.js +272 -0
  71. package/core/dist/runtime-v2/internalization/owner-resolution-service.js.map +1 -0
  72. package/core/dist/runtime-v2/internalization/owner-retry.d.ts +12 -0
  73. package/core/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -1
  74. package/core/dist/runtime-v2/internalization/owner-retry.js +23 -0
  75. package/core/dist/runtime-v2/internalization/owner-retry.js.map +1 -1
  76. package/core/dist/runtime-v2/internalization/owner-review.d.ts +195 -0
  77. package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -0
  78. package/core/dist/runtime-v2/internalization/owner-review.js +470 -0
  79. package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -0
  80. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +5 -1
  81. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
  82. package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
  83. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +68 -0
  84. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
  85. package/core/dist/runtime-v2/internalization/pitask-metadata.js +104 -0
  86. package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
  87. package/core/dist/runtime-v2/internalization/revision-reopen.d.ts +56 -0
  88. package/core/dist/runtime-v2/internalization/revision-reopen.d.ts.map +1 -0
  89. package/core/dist/runtime-v2/internalization/revision-reopen.js +144 -0
  90. package/core/dist/runtime-v2/internalization/revision-reopen.js.map +1 -0
  91. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +12 -0
  92. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
  93. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js +145 -14
  94. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
  95. package/core/dist/runtime-v2/store/context/sqlite-context-assembler.test.js +1 -0
  96. package/core/dist/runtime-v2/store/context/sqlite-context-assembler.test.js.map +1 -1
  97. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +6 -0
  98. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
  99. package/core/dist/runtime-v2/store/runtime-state-manager.js +9 -0
  100. package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
  101. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.d.ts +2 -0
  102. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.d.ts.map +1 -0
  103. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js +78 -0
  104. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js.map +1 -0
  105. package/core/dist/runtime-v2/store/task/memory-task-store.d.ts +1 -0
  106. package/core/dist/runtime-v2/store/task/memory-task-store.d.ts.map +1 -1
  107. package/core/dist/runtime-v2/store/task/memory-task-store.js +8 -0
  108. package/core/dist/runtime-v2/store/task/memory-task-store.js.map +1 -1
  109. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +1 -0
  110. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  111. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +52 -0
  112. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  113. package/core/dist/runtime-v2/store/task/task-store.d.ts +9 -0
  114. package/core/dist/runtime-v2/store/task/task-store.d.ts.map +1 -1
  115. package/core/dist/runtime-v2/store/workspace-isolation.test.js +1 -0
  116. package/core/dist/runtime-v2/store/workspace-isolation.test.js.map +1 -1
  117. package/package.json +2 -2
  118. package/pd-cli/dist/commands/runtime-internalization-retry.d.ts.map +1 -1
  119. package/pd-cli/dist/commands/runtime-internalization-retry.js +14 -0
  120. package/pd-cli/dist/commands/runtime-internalization-retry.js.map +1 -1
  121. package/plugin/dist/bundle.js +580 -569
  122. package/plugin/dist/governance-audit.js +153 -153
  123. package/plugin/dist/rulehost-evidence.js +180 -180
  124. package/plugin/openclaw.plugin.json +1 -1
  125. package/plugin/package.json +1 -1
@@ -0,0 +1,186 @@
1
+ import { createRuntimeStateHandle, applyOwnerResolution, } from '@principles/core/runtime-v2';
2
+ import { OwnerDecisionConsoleModel } from '../models/OwnerDecisionConsoleModel.js';
3
+ import { sendSuccess, sendError, sendMethodNotAllowed, sendBadRequest } from '../utils/response.js';
4
+ import { readBody } from '../utils/request.js';
5
+ const models = new Map();
6
+ function getModel(workspaceDir) {
7
+ let model = models.get(workspaceDir);
8
+ if (!model) {
9
+ model = new OwnerDecisionConsoleModel(workspaceDir);
10
+ models.set(workspaceDir, model);
11
+ }
12
+ return model;
13
+ }
14
+ const RESOLVE_ACTIONS = new Set(['accept_current', 'revise_once', 'reject_current']);
15
+ /** rc-1/rc-2/rc-3: body 按未知值逐字段校验,非法即 400 fail-loud。 */
16
+ function parseResolveBody(raw) {
17
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
18
+ return { ok: false, error: 'Request body must be a JSON object.' };
19
+ }
20
+ const b = raw;
21
+ if (typeof b.action !== 'string' || !RESOLVE_ACTIONS.has(b.action)) {
22
+ return { ok: false, error: `action must be one of accept_current/revise_once/reject_current, got ${String(b.action)}` };
23
+ }
24
+ for (const field of ['reviewKey', 'expectedSourceRunId', 'expectedSourceArtifactId', 'expectedSourceArtifactHash']) {
25
+ const v = b[field];
26
+ if (typeof v !== 'string' || v.trim() === '') {
27
+ return { ok: false, error: `${field} must be a non-empty string.` };
28
+ }
29
+ }
30
+ if (typeof b.expectedRevisionEpoch !== 'number' || !Number.isInteger(b.expectedRevisionEpoch) || b.expectedRevisionEpoch < 0) {
31
+ return { ok: false, error: 'expectedRevisionEpoch must be a non-negative integer.' };
32
+ }
33
+ if (b.ownerInstruction !== undefined && b.ownerInstruction !== null
34
+ && (typeof b.ownerInstruction !== 'string' || b.ownerInstruction.length > 600)) {
35
+ return { ok: false, error: 'ownerInstruction must be a string of at most 600 characters (or null).' };
36
+ }
37
+ const { reviewKey } = b;
38
+ const { expectedSourceRunId } = b;
39
+ const { expectedSourceArtifactId } = b;
40
+ const { expectedSourceArtifactHash } = b;
41
+ if (typeof reviewKey !== 'string' || typeof expectedSourceRunId !== 'string'
42
+ || typeof expectedSourceArtifactId !== 'string' || typeof expectedSourceArtifactHash !== 'string') {
43
+ return { ok: false, error: 'unreachable: string fields validated above' };
44
+ }
45
+ return {
46
+ ok: true,
47
+ body: {
48
+ action: b.action,
49
+ reviewKey,
50
+ expectedRevisionEpoch: b.expectedRevisionEpoch,
51
+ expectedSourceRunId,
52
+ expectedSourceArtifactId,
53
+ expectedSourceArtifactHash,
54
+ ...(typeof b.ownerInstruction === 'string' ? { ownerInstruction: b.ownerInstruction } : {}),
55
+ },
56
+ };
57
+ }
58
+ /** OwnerResolutionOutcome → HTTP 响应映射(cli-6: 结构化 reason + nextAction)。 */
59
+ function sendOutcome(res, outcome) {
60
+ switch (outcome.status) {
61
+ case 'resolved':
62
+ sendSuccess(res, {
63
+ status: 'resolved',
64
+ resolutionId: outcome.resolutionId,
65
+ reviewKey: outcome.reviewKey,
66
+ action: outcome.action,
67
+ applied: outcome.applied,
68
+ ...(outcome.effectiveDecision !== undefined ? { effectiveDecision: outcome.effectiveDecision } : {}),
69
+ ...(outcome.targetTaskId !== undefined ? { targetTaskId: outcome.targetTaskId } : {}),
70
+ runnerWillApply: outcome.runnerWillApply,
71
+ nextAction: outcome.applied
72
+ ? 'Resolution applied. Refresh the governance focus.'
73
+ : 'Resolution recorded durably; the pipeline runner will apply it without re-invoking the LLM. Refresh the governance focus.',
74
+ });
75
+ return;
76
+ case 'not_found':
77
+ sendError(res, 404, 'task_not_found', 'Owner decision task not found.');
78
+ return;
79
+ case 'metadata_invalid':
80
+ sendError(res, 409, 'metadata_invalid', 'Task metadata failed PI hydration; refusing to resolve.', {
81
+ nextAction: 'Inspect the task metadata: pd runtime internalization integrity --json',
82
+ });
83
+ return;
84
+ case 'not_decision_capable':
85
+ sendError(res, 409, 'not_decision_capable', 'This task is not currently decision-capable.', {
86
+ nextAction: 'Refresh the governance focus — the decision facts may have changed.',
87
+ blockers: outcome.blockers,
88
+ });
89
+ return;
90
+ case 'stale_owner_decision':
91
+ sendError(res, 409, 'stale_owner_decision', 'The durable decision facts changed since this item was rendered. The decision was NOT applied.', {
92
+ nextAction: 'Refresh the governance focus and decide again on the current version.',
93
+ });
94
+ return;
95
+ case 'already_resolved':
96
+ sendError(res, 409, 'already_resolved', `This decision was already resolved with action '${outcome.existingAction}'.`, {
97
+ nextAction: 'Refresh the governance focus.',
98
+ existingAction: outcome.existingAction,
99
+ });
100
+ return;
101
+ case 'revise_target_unresolved':
102
+ sendError(res, 409, 'revise_target_unresolved', 'The revision target task could not be resolved from the lineage.', {
103
+ nextAction: 'Inspect the task lineage; this is a recovery-class failure.',
104
+ });
105
+ return;
106
+ case 'revise_reopen_failed':
107
+ sendError(res, 409, 'revise_reopen_failed', `Reopening the revision target failed: ${outcome.reason}`, {
108
+ nextAction: 'Retry after the target task is no longer in flight, or inspect the task states.',
109
+ reason: outcome.reason,
110
+ });
111
+ return;
112
+ case 'cas_conflict':
113
+ sendError(res, 409, 'cas_conflict', 'Concurrent write detected while recording the resolution.', {
114
+ nextAction: 'Retry the request — the operation is idempotent.',
115
+ });
116
+ return;
117
+ default: {
118
+ // exhaustive guard
119
+ const exhaustive = outcome;
120
+ sendError(res, 500, 'owner_resolution_error', `Unhandled outcome: ${JSON.stringify(exhaustive)}`);
121
+ }
122
+ }
123
+ }
124
+ export async function handleOwnerDecisionsRoute(req, res, ctx) {
125
+ const { workspaceDir, subPath, ownerIdentity } = ctx;
126
+ const method = req.method ?? 'GET';
127
+ // GET /api/v1/governance/owner-decisions — list
128
+ if (subPath === '' || subPath === '/') {
129
+ if (method !== 'GET') {
130
+ sendMethodNotAllowed(res);
131
+ return;
132
+ }
133
+ try {
134
+ const result = await getModel(workspaceDir).listOwnerDecisionItems();
135
+ sendSuccess(res, result);
136
+ }
137
+ catch (err) {
138
+ const message = err instanceof Error ? err.message : String(err);
139
+ sendError(res, 500, 'owner_decisions_list_error', message, {
140
+ nextAction: 'Inspect the workspace state (.pd/state.db) and retry.',
141
+ });
142
+ }
143
+ return;
144
+ }
145
+ // POST /api/v1/governance/owner-decisions/:taskId/resolve
146
+ const resolveMatch = /^\/([^/]+)\/resolve$/.exec(subPath);
147
+ if (resolveMatch) {
148
+ if (method !== 'POST') {
149
+ sendMethodNotAllowed(res);
150
+ return;
151
+ }
152
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- match group is always present
153
+ const taskId = decodeURIComponent(resolveMatch[1]);
154
+ let body;
155
+ try {
156
+ const raw = await readBody(req);
157
+ body = raw.trim() === '' ? {} : JSON.parse(raw);
158
+ }
159
+ catch {
160
+ sendBadRequest(res, 'Request body must be valid JSON.');
161
+ return;
162
+ }
163
+ const parsed = parseResolveBody(body);
164
+ if (!parsed.ok) {
165
+ sendBadRequest(res, parsed.error);
166
+ return;
167
+ }
168
+ // 应用走 core 单一服务(writable RuntimeStateManager handle)
169
+ const handle = await createRuntimeStateHandle({ workspaceDir });
170
+ try {
171
+ const outcome = await applyOwnerResolution({ stateManager: handle.stateManager }, { taskId, request: parsed.body, identity: ownerIdentity });
172
+ sendOutcome(res, outcome);
173
+ }
174
+ catch (err) {
175
+ const message = err instanceof Error ? err.message : String(err);
176
+ sendError(res, 500, 'owner_resolution_error', message, {
177
+ nextAction: 'Inspect the workspace state (.pd/state.db) and retry.',
178
+ });
179
+ }
180
+ finally {
181
+ await handle.close();
182
+ }
183
+ return;
184
+ }
185
+ sendError(res, 404, 'not_found', `Route /api/v1/governance/owner-decisions${subPath} not found`);
186
+ }
@@ -42,7 +42,7 @@ async function getDecidedPrincipleIds(workspaceDir) {
42
42
  // Runtime V2 uses <workspace>/.pd/state.db (NOT .state/state.db)
43
43
  const dbPath = path.join(workspaceDir, '.pd', 'state.db');
44
44
  if (!fs.existsSync(dbPath)) {
45
- return { ids: new Set(), unavailableReason: 'approval_db_not_found' };
45
+ return { ids: new Set(), pendingIds: new Set(), unavailableReason: 'approval_db_not_found' };
46
46
  }
47
47
  let closeFn = null;
48
48
  try {
@@ -51,25 +51,33 @@ async function getDecidedPrincipleIds(workspaceDir) {
51
51
  closeFn = () => conn.close();
52
52
  const db = conn.getDb();
53
53
  // Join approvals with pi_artifacts to find decided principle IDs
54
- const rows = db.prepare("SELECT DISTINCT a.source_principle_id " +
54
+ // PRI-629: also resolve PENDING approvals — a pending approval is a real
55
+ // Owner decision (owner_actionable), unlike candidate lifecycle (in_pipeline).
56
+ const rows = db.prepare("SELECT DISTINCT a.source_principle_id, ap.status " +
55
57
  "FROM approvals ap " +
56
58
  "JOIN pi_artifacts a ON a.artifact_id = ap.artifact_id " +
57
- "WHERE ap.status IN ('approved', 'rejected') " +
59
+ "WHERE ap.status IN ('approved', 'rejected', 'pending') " +
58
60
  "AND a.source_principle_id IS NOT NULL").all();
59
61
  // EP-01 Rule 1: treat DB rows as unknown; validate before use
60
62
  // EP-01 Rule 2: no `as` cast — use getOwnStringField instead
61
63
  const ids = new Set();
64
+ const pendingIds = new Set();
62
65
  for (const row of rows) {
63
66
  const val = getOwnStringField(row, 'source_principle_id');
64
- if (val !== undefined) {
67
+ if (val === undefined)
68
+ continue;
69
+ if (getOwnStringField(row, 'status') === 'pending') {
70
+ pendingIds.add(val);
71
+ }
72
+ else {
65
73
  ids.add(val);
66
74
  }
67
75
  }
68
- return { ids };
76
+ return { ids, pendingIds };
69
77
  }
70
78
  catch (err) {
71
79
  const message = err instanceof Error ? err.message : String(err);
72
- return { ids: new Set(), unavailableReason: `approval_query_failed: ${message}` };
80
+ return { ids: new Set(), pendingIds: new Set(), unavailableReason: `approval_query_failed: ${message}` };
73
81
  }
74
82
  finally {
75
83
  closeFn?.();
@@ -197,7 +205,7 @@ export async function handlePrinciplesRoute({ req, res, workspaceDir, subPath, f
197
205
  const VALID_FILTERS = new Set(['all', 'actionable']);
198
206
  const filter = (filterRaw !== null && VALID_FILTERS.has(filterRaw) ? filterRaw : 'actionable');
199
207
  const decidedResult = await getDecidedPrincipleIds(workspaceDir);
200
- const result = await model.listPrinciples(filter, decidedResult.ids);
208
+ const result = await model.listPrinciples(filter, decidedResult.ids, decidedResult.pendingIds);
201
209
  // Surface approval cross-check unavailability to the UI (ERR-002)
202
210
  if (decidedResult.unavailableReason) {
203
211
  result.approvalCrossCheckUnavailable = decidedResult.unavailableReason;
@@ -1,4 +1,5 @@
1
1
  import type { ApiResponse } from "../types.js";
2
+ import type { OwnerDecisionsData, OwnerResolutionResultData } from "./utils/validators.js";
2
3
  import { listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm } from "./utils/signal-keywords-api.js";
3
4
  import type { GovernanceExperienceSnapshot, OwnerGovernanceView } from '@principles/core/runtime-v2';
4
5
  import type { OwnerIdentityViewData, OwnerIdentityRegisterData, OwnerIdentityUnregisterData } from "./utils/validators.js";
@@ -148,6 +149,16 @@ declare function updateOutputLanguage(outputLanguage: string): Promise<ApiRespon
148
149
  declare function fetchGovernanceQueue(): Promise<ApiResponse<GovernanceQueueData>>;
149
150
  declare function fetchGovernanceExperience(): Promise<ApiResponse<GovernanceExperienceSnapshot>>;
150
151
  declare function recoverFailedTask(taskId: string, reason?: string, force?: boolean): Promise<ApiResponse<RecoveryResultData>>;
152
+ declare function fetchOwnerDecisions(): Promise<ApiResponse<OwnerDecisionsData>>;
153
+ declare function resolveOwnerDecision(taskId: string, body: {
154
+ action: 'accept_current' | 'revise_once' | 'reject_current';
155
+ reviewKey: string;
156
+ expectedRevisionEpoch: number;
157
+ expectedSourceRunId: string;
158
+ expectedSourceArtifactId: string;
159
+ expectedSourceArtifactHash: string;
160
+ ownerInstruction?: string | null;
161
+ }): Promise<ApiResponse<OwnerResolutionResultData>>;
151
162
  declare function fetchApprovalsGrouped(): Promise<ApiResponse<ApprovalsGroupedData>>;
152
163
  declare function fetchAllActivations(): Promise<ApiResponse<ActivationsData>>;
153
164
  declare function disableActivation(activationId: string): Promise<ApiResponse<DisableActivationData>>;
@@ -214,7 +225,7 @@ export interface FollowUpPayload {
214
225
  candidateId?: string;
215
226
  }
216
227
  declare function dispatchFollowUp(decisionId: string, payload: FollowUpPayload): Promise<ApiResponse<FollowUpResponseData>>;
217
- export { request, listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
228
+ export { request, listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
218
229
  export type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, RecoveryResultData, ActivationsData, DisableActivationData, RuleCodeOwnerReviewData, RuleCodeMutationData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, RollbackResultData, ApprovalRecordData, PrinciplesListData, ApprovalsGroupedData, EvidenceChainData, EvidenceChainRecordData, EvidenceChainStateData, TrajectoryData, TrajectoryStageData, IntentSummaryData, IntentSectionsData, IntentDocWarningData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, LinkCandidateFollowUpData, GuideRulehostFollowUpData, GeneratePatchProposalFollowUpData, IntentVersionEntry, IntentVersionData, } from "./utils/validators.js";
219
230
  export type { SignalKeyword, PendingSignalTerm } from "./utils/signal-keywords-types.js";
220
231
  export type { ActivationRecordData as ActivationRecord } from "./utils/validators.js";
@@ -1,5 +1,5 @@
1
1
  import { listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, } from "./utils/signal-keywords-api.js";
2
- import { validateErrorResponse, validateHeaders, validateFeedbackReport, validateFeedbackDraftsList, validateFeedbackDraftEnvelope, validateFeedbackChannels, validateFeedbackSubmitResult, validateDeleteEnvelope, validateWorkspaceEntry, validateWorkspaceList, validateRemovedEnvelope, validateSyncResult, validateConfigSummary, validateConfigCatalog, validateAgentBindingUpdate, validateDefaultRuntimeUpdate, validateRuntimeProfileMutation, validateReadinessCheck, validateFeatureFlagUpdate, validateOutputLanguage, validateGovernanceQueue, validateRecoveryResult, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateApprovalsGrouped, validateEvidenceChain, validateTrajectoryData, validateIntentSummary, validateIntentDecisionList, validateIntentDecisionResult, validateIntentDecisionSummary, validateFollowUpResponse, validateIntentInitResult, validateIntentSaveResult, validateIntentRawContent, validateIntentVersions, validateOwnerGovernanceView, validateGovernanceExperienceSnapshot, validateRuleCodeOwnerReview, validateRuleCodeMutation, validateOwnerIdentityView, validateOwnerIdentityRegister, validateOwnerIdentityUnregister, } from "./utils/validators.js";
2
+ import { validateErrorResponse, validateHeaders, validateFeedbackReport, validateFeedbackDraftsList, validateFeedbackDraftEnvelope, validateFeedbackChannels, validateFeedbackSubmitResult, validateDeleteEnvelope, validateWorkspaceEntry, validateWorkspaceList, validateRemovedEnvelope, validateSyncResult, validateConfigSummary, validateConfigCatalog, validateAgentBindingUpdate, validateDefaultRuntimeUpdate, validateRuntimeProfileMutation, validateReadinessCheck, validateFeatureFlagUpdate, validateOutputLanguage, validateGovernanceQueue, validateRecoveryResult, validateOwnerDecisionsData, validateOwnerResolutionResult, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateApprovalsGrouped, validateEvidenceChain, validateTrajectoryData, validateIntentSummary, validateIntentDecisionList, validateIntentDecisionResult, validateIntentDecisionSummary, validateFollowUpResponse, validateIntentInitResult, validateIntentSaveResult, validateIntentRawContent, validateIntentVersions, validateOwnerGovernanceView, validateGovernanceExperienceSnapshot, validateRuleCodeOwnerReview, validateRuleCodeMutation, validateOwnerIdentityView, validateOwnerIdentityRegister, validateOwnerIdentityUnregister, } from "./utils/validators.js";
3
3
  // ── Auth ──────────────────────────────────────────────────────────────────────
4
4
  function getToken() {
5
5
  return sessionStorage.getItem("pd_token");
@@ -454,6 +454,13 @@ async function recoverFailedTask(taskId, reason, force) {
454
454
  body: JSON.stringify(body),
455
455
  }, validateRecoveryResult);
456
456
  }
457
+ // PRI-629: unified Owner Decision inbox — read projection + resolution.
458
+ async function fetchOwnerDecisions() {
459
+ return request('/api/v1/governance/owner-decisions', undefined, validateOwnerDecisionsData);
460
+ }
461
+ async function resolveOwnerDecision(taskId, body) {
462
+ return request('/api/v1/governance/owner-decisions/' + encodeURIComponent(taskId) + '/resolve', { method: 'POST', body: JSON.stringify(body) }, validateOwnerResolutionResult);
463
+ }
457
464
  async function fetchApprovalsGrouped() {
458
465
  return request('/api/v1/approvals/grouped', undefined, validateApprovalsGrouped);
459
466
  }
@@ -570,4 +577,4 @@ listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendin
570
577
  // auth
571
578
  getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport,
572
579
  // Feedback submit ladder (Slice 3, spec §8)
573
- fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
580
+ fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useEffect, useRef, useState, } from 'react';
3
- import { fetchGovernanceQueue } from '../../api.js';
3
+ import { fetchGovernanceQueue, fetchOwnerDecisions } from '../../api.js';
4
4
  import { useNotificationSound } from '../../hooks/useNotificationSound.js';
5
5
  import { diffNotificationCounts } from './notification-reducer.js';
6
6
  import { loadSoundEnabled, saveSoundEnabled } from './sound-storage.js';
@@ -33,8 +33,14 @@ export function NotificationProvider({ children }) {
33
33
  console.warn('Notification poll failed:', result.error);
34
34
  return;
35
35
  }
36
- const pendingCount = result.data.pendingReviewCount;
37
36
  const degradedCount = result.data.degradedSignals?.length ?? 0;
37
+ // PRI-629 (SPEC §27): 侧边栏徽标 N = 真实可执行 Owner 决策数
38
+ // (OwnerDecisionItem.length) — 不是 approvals pending / candidate /
39
+ // needs_human_review 计数。投影失败时回退 approvals pending (保守)。
40
+ const decisionsResult = await fetchOwnerDecisions();
41
+ const pendingCount = decisionsResult.success
42
+ ? decisionsResult.data.total
43
+ : result.data.pendingReviewCount;
38
44
  hasSuccessfulPollRef.current = true;
39
45
  setState((prev) => ({
40
46
  ...prev,
@@ -260,7 +260,36 @@
260
260
  },
261
261
  "zeroStateHealthy": "No items need your judgment right now. PD has checked behavior evidence, principle review queue, and system signals.",
262
262
  "zeroStateDbMissing": "PD has not run in this workspace yet. Once the state database is initialized, governance data will appear here.",
263
- "evidenceInProgress": "{{count}} behavior evidence item(s) recorded, being diagnosed and processed."
263
+ "evidenceInProgress": "{{count}} behavior evidence item(s) recorded, being diagnosed and processed.",
264
+ "ownerDecision": {
265
+ "sectionTitle": "Needs your decision",
266
+ "empty": "Nothing needs your decision right now. Problems the machine can solve on its own will not disturb you.",
267
+ "unavailable": "(unavailable)",
268
+ "loadError": "The decision list is temporarily unavailable — the governance summary is still shown.",
269
+ "kindEvaluator": "Evaluator review",
270
+ "kindRollout": "Rollout review",
271
+ "kindApproval": "Deployment approval",
272
+ "kindRulecode": "RuleCode decision",
273
+ "legacyBadge": "Inferred from history",
274
+ "machineRecommendationLabel": "Machine recommendation",
275
+ "rolloutSafetyNote": "Accept this review and continue. High-risk rules still require an independent deployment approval.",
276
+ "acceptCurrent": "Accept current version",
277
+ "reviseOnce": "Revise once more",
278
+ "rejectCurrent": "Reject",
279
+ "instructionLabel": "Revision guidance (optional, one sentence)",
280
+ "instructionPlaceholder": "e.g. Do not add new tool-naming requirements; only address the unresolved items from the last round.",
281
+ "instructionRequired": "Fill in the revision guidance, or collapse the input and submit directly.",
282
+ "actionConsequence": "Accept = continue with the current version (the machine verdict is preserved permanently); Revise once = authorize one extra revision round (automatic budget unchanged); Reject = terminate this evolution.",
283
+ "advanced": "Advanced diagnostics",
284
+ "resolved": "Resolution recorded.",
285
+ "staleError": "The facts of this item changed (a new evaluation may exist). Your decision was NOT applied — refresh and decide again.",
286
+ "alreadyResolved": "This decision was already handled; no repeat action needed.",
287
+ "actionFailed": "Action failed, please retry later.",
288
+ "approvalHint": "The deployment approval for this principle is still waiting in the approval queue.",
289
+ "rulecodeHint": "This rule is in shadow activation and needs a decision in activation management.",
290
+ "goApprovalsCta": "Go to deployment approvals",
291
+ "goActivationCta": "Go to activation management"
292
+ }
264
293
  },
265
294
  "pain": {
266
295
  "eyebrow": "Behavior Evidence",
@@ -477,6 +506,7 @@
477
506
  "statusApproved": "Approved",
478
507
  "statusRejected": "Rejected",
479
508
  "statusParked": "Parked",
509
+ "statusCandidate": "Internalizing",
480
510
  "emptyTitle": "No principles to review",
481
511
  "emptyDescription": "When PD captures behavior deviation signals, principle candidates will appear here for your review.",
482
512
  "emptyActionable": "No owner-actionable principles at this time. When PD captures behavior deviation signals, principle candidates will appear here for your review. You can check the Behavior Evidence page to see if PD is actively collecting signals.",
@@ -1576,6 +1606,8 @@
1576
1606
  "recoverConfirmButton": "Recover",
1577
1607
  "recoverSuccess": "Recovery accepted — the task is pending again",
1578
1608
  "recoverFailed": "Recovery failed",
1609
+ "awaitingOwnerDecision": "Awaiting owner decision",
1610
+ "goGovernanceFocus": "Go to governance focus",
1579
1611
  "taskId": "Task ID",
1580
1612
  "painId": "Pain ID",
1581
1613
  "kind": "Kind",
@@ -260,7 +260,36 @@
260
260
  },
261
261
  "zeroStateHealthy": "当前没有需要你判断的事项。PD 已检查行为证据、原则审查队列和系统信号。",
262
262
  "zeroStateDbMissing": "PD 尚未在此工作空间中运行。状态数据库初始化后,治理数据将在这里呈现。",
263
- "evidenceInProgress": "{{count}} 条行为证据已记录,正在诊断和处理中。"
263
+ "evidenceInProgress": "{{count}} 条行为证据已记录,正在诊断和处理中。",
264
+ "ownerDecision": {
265
+ "sectionTitle": "需要你决定",
266
+ "empty": "现在没有需要你决定的事项。机器能自己解决的问题不会打扰你。",
267
+ "unavailable": "(暂不可用)",
268
+ "loadError": "决策列表暂时不可用——治理摘要仍可查看。",
269
+ "kindEvaluator": "Evaluator 评审",
270
+ "kindRollout": "Rollout 评审",
271
+ "kindApproval": "部署审批",
272
+ "kindRulecode": "RuleCode 决策",
273
+ "legacyBadge": "历史数据推断",
274
+ "machineRecommendationLabel": "机器建议",
275
+ "rolloutSafetyNote": "接受当前评审并继续。高风险规则仍需经过独立部署审批。",
276
+ "acceptCurrent": "接受当前版本",
277
+ "reviseOnce": "再修改一次",
278
+ "rejectCurrent": "拒绝",
279
+ "instructionLabel": "修订指导(可选,一句话)",
280
+ "instructionPlaceholder": "例如:不要增加新的工具命名要求,只处理上一轮未解决问题。",
281
+ "instructionRequired": "请填写修订指导,或收起输入后直接提交。",
282
+ "actionConsequence": "接受 = 按当前版本继续(机器原判定永久保留);再修改一次 = 授权额外一轮修订(自动预算不变);拒绝 = 终止本条演化。",
283
+ "advanced": "高级诊断",
284
+ "resolved": "裁决已记录。",
285
+ "staleError": "该事项的事实已变化(可能有新的评估结果)。未执行你的决定,请刷新后重新裁决。",
286
+ "alreadyResolved": "该决策已被处理过,无需重复操作。",
287
+ "actionFailed": "操作失败,请稍后重试。",
288
+ "approvalHint": "该原则的部署审批仍在审批队列中等待处理。",
289
+ "rulecodeHint": "该规则处于影子激活期,需要在激活管理中裁决。",
290
+ "goApprovalsCta": "前往部署审批",
291
+ "goActivationCta": "前往激活管理"
292
+ }
264
293
  },
265
294
  "pain": {
266
295
  "eyebrow": "行为证据",
@@ -477,6 +506,7 @@
477
506
  "statusApproved": "已批准",
478
507
  "statusRejected": "已拒绝",
479
508
  "statusParked": "已暂存",
509
+ "statusCandidate": "内化中",
480
510
  "emptyTitle": "还没有可审查原则",
481
511
  "emptyDescription": "当 PD 捕获到行为偏差信号时,会在这里生成原则候选,等待你审查。",
482
512
  "emptyActionable": "当前没有待决策的原则。当 PD 捕获到行为偏差信号时,原则候选会出现在这里。你可以前往行为证据页查看 PD 是否正在收集信号。",
@@ -1576,6 +1606,8 @@
1576
1606
  "recoverConfirmButton": "恢复",
1577
1607
  "recoverSuccess": "恢复已接受——任务已重新进入待处理状态",
1578
1608
  "recoverFailed": "恢复失败",
1609
+ "awaitingOwnerDecision": "等待拥有者决策",
1610
+ "goGovernanceFocus": "前往治理焦点",
1579
1611
  "taskId": "任务 ID",
1580
1612
  "painId": "Pain ID",
1581
1613
  "kind": "类型",
@@ -88,6 +88,9 @@ function validateTask(v) {
88
88
  const painId = readNullableString(v, "painId");
89
89
  const lastError = readNullableString(v, "lastError");
90
90
  const lastAttemptAt = readNullableString(v, "lastAttemptAt");
91
+ const ownerDecisionRequired = Object.hasOwn(v, "ownerDecisionRequired")
92
+ ? v.ownerDecisionRequired === true
93
+ : undefined;
91
94
  return {
92
95
  taskId,
93
96
  taskKind,
@@ -98,6 +101,7 @@ function validateTask(v) {
98
101
  maxAttempts,
99
102
  createdAt,
100
103
  lastAttemptAt,
104
+ ...(ownerDecisionRequired !== undefined ? { ownerDecisionRequired } : {}),
101
105
  };
102
106
  }
103
107
  function validateFailedTasksData(v) {
@@ -318,5 +322,5 @@ function LoadedContent({ data, onCreateDraft, onRecover, t }) {
318
322
  .replace("{total}", String(data.total)) }))] })] }));
319
323
  }
320
324
  function TaskTable({ tasks, onCreateDraft, onRecover, t }) {
321
- return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 pb-2 border-b border-line font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { children: t("pages.failedTasks.taskId") }), _jsx("span", { children: t("pages.failedTasks.painId") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.lastError") }), _jsx("span", { children: t("pages.failedTasks.attempts") }), _jsx("span", { children: t("pages.failedTasks.lastAttempt") }), _jsx("span", { "aria-hidden": "true" })] }), tasks.map((task) => (_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 py-2 items-center text-[13px] border-b border-line/50 last:border-b-0", children: [_jsx("span", { className: "font-mono text-ink-2 truncate", title: task.taskId, children: truncateId(task.taskId) }), _jsx("span", { className: "font-mono text-ink-3 truncate", title: task.painId ?? "", children: task.painId ? truncateId(task.painId) : t("pages.failedTasks.noPainId") }), _jsx(Badge, { variant: statusToVariant(task.status), children: task.status }), _jsx("span", { className: "text-ink-3 truncate", title: task.lastError ?? "", children: task.lastError ?? "—" }), _jsx("span", { className: "font-mono text-ink-2 text-center", children: task.attemptCount }), _jsx("span", { className: "font-mono text-ink-3 text-[12px]", children: task.lastAttemptAt ? formatDate(task.lastAttemptAt) : "—" }), onRecover && (_jsx(Button, { variant: "default", size: "sm", onClick: () => onRecover(task), className: "whitespace-nowrap", children: t("pages.failedTasks.recover") })), _jsx(Button, { variant: "outline", size: "sm", onClick: () => onCreateDraft(task), className: "whitespace-nowrap", children: t("pages.failedTasks.createDraft") })] }, task.taskId)))] }));
325
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 pb-2 border-b border-line font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { children: t("pages.failedTasks.taskId") }), _jsx("span", { children: t("pages.failedTasks.painId") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.lastError") }), _jsx("span", { children: t("pages.failedTasks.attempts") }), _jsx("span", { children: t("pages.failedTasks.lastAttempt") }), _jsx("span", { "aria-hidden": "true" })] }), tasks.map((task) => (_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 py-2 items-center text-[13px] border-b border-line/50 last:border-b-0", children: [_jsx("span", { className: "font-mono text-ink-2 truncate", title: task.taskId, children: truncateId(task.taskId) }), _jsx("span", { className: "font-mono text-ink-3 truncate", title: task.painId ?? "", children: task.painId ? truncateId(task.painId) : t("pages.failedTasks.noPainId") }), _jsx(Badge, { variant: statusToVariant(task.status), children: task.status }), _jsx("span", { className: "text-ink-3 truncate", title: task.lastError ?? "", children: task.lastError ?? "—" }), _jsx("span", { className: "font-mono text-ink-2 text-center", children: task.attemptCount }), _jsx("span", { className: "font-mono text-ink-3 text-[12px]", children: task.lastAttemptAt ? formatDate(task.lastAttemptAt) : "—" }), task.ownerDecisionRequired === true ? (_jsxs("span", { className: "whitespace-nowrap text-[12px]", children: [_jsx("span", { className: "text-amber", children: t("pages.failedTasks.awaitingOwnerDecision") }), " ", _jsx("a", { href: "#/focus", className: "text-gov underline underline-offset-2 hover:text-gov/80", "data-testid": `go-focus-${task.taskId}`, children: t("pages.failedTasks.goGovernanceFocus") })] })) : onRecover ? (_jsx(Button, { variant: "default", size: "sm", onClick: () => onRecover(task), className: "whitespace-nowrap", children: t("pages.failedTasks.recover") })) : null, _jsx(Button, { variant: "outline", size: "sm", onClick: () => onCreateDraft(task), className: "whitespace-nowrap", children: t("pages.failedTasks.createDraft") })] }, task.taskId)))] }));
322
326
  }
@@ -8,7 +8,8 @@ import { PageLoading } from "../../components/layout/page-loading.js";
8
8
  import { SectionTitle } from "../../components/layout/section-title.js";
9
9
  import { DailyThoughtCard } from "../../components/focus/daily-thought-card.js";
10
10
  import { ShinyText } from "../../components/ui/shiny-text.js";
11
- import { fetchGovernanceQueue, fetchApprovalsGrouped, approveApproval, rejectApproval, editApproval, fetchAllActivations, } from "../../api.js";
11
+ import { fetchGovernanceQueue, fetchApprovalsGrouped, approveApproval, rejectApproval, editApproval, fetchAllActivations, fetchOwnerDecisions, } from "../../api.js";
12
+ import { OwnerDecisionCard } from "./OwnerDecisionCard.js";
12
13
  import { fetchGovernanceExperience } from "../../api.js";
13
14
  export function summarizeDecisionResults(results) {
14
15
  const failures = results.filter((result) => !result.success);
@@ -458,6 +459,9 @@ export function FocusPage({ featureFlags }) {
458
459
  const [groupedErrorReason, setGroupedErrorReason] = useState(null);
459
460
  const [ruleCodeItems, setRuleCodeItems] = useState([]);
460
461
  const [ruleCodeErrorReason, setRuleCodeErrorReason] = useState(null);
462
+ // PRI-629: 统一 Owner Inbox — 两种模式都加载 (决策与治理摘要模式正交)
463
+ const [ownerDecisionItems, setOwnerDecisionItems] = useState([]);
464
+ const [ownerDecisionError, setOwnerDecisionError] = useState(null);
461
465
  const loadData = useCallback(async () => {
462
466
  setLoadingState("loading");
463
467
  setErrorMessage(null);
@@ -507,6 +511,17 @@ export function FocusPage({ featureFlags }) {
507
511
  setRuleCodeItems([]);
508
512
  setRuleCodeErrorReason(activationsResult.error);
509
513
  }
514
+ // PRI-629: 统一 Owner Inbox (decision-capable NHR + approvals + rulecode)。
515
+ // 投影失败不阻塞页面 (rc-9 降级 — 显示原因,治理摘要仍可用)。
516
+ const decisionsResult = await fetchOwnerDecisions();
517
+ if (decisionsResult.success) {
518
+ setOwnerDecisionItems(decisionsResult.data.items);
519
+ setOwnerDecisionError(null);
520
+ }
521
+ else {
522
+ setOwnerDecisionItems([]);
523
+ setOwnerDecisionError(decisionsResult.error ?? "Owner decisions unavailable");
524
+ }
510
525
  setLoadingState("loaded");
511
526
  }, [experienceMode, t]);
512
527
  useEffect(() => {
@@ -551,7 +566,9 @@ export function FocusPage({ featureFlags }) {
551
566
  ? "bg-amber/10 text-amber border border-amber/20"
552
567
  : governanceState === "in_progress"
553
568
  ? "bg-green/10 text-green border border-green/20"
554
- : "text-ink-4 border border-line bg-surface/80"}`, role: "status", children: t(`pages.focus.stateLabel.${governanceState}`) }) }), _jsx("div", { className: "text-ink-2 text-[13px] leading-relaxed mt-1", children: stateReason }), _jsxs("div", { className: "text-ink-4 text-[13px] leading-relaxed mt-1", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.nextActionLabel") }), " ", nextAction] })] })) : null, !experienceMode && (_jsxs(_Fragment, { children: [_jsx(ProseSummary, { pendingCount: pendingCount, deviationCount: deviationCount, stagnationCount: stagnationCount, evidenceCount: evidenceCount }), _jsx(FeedbackStratification, { gateBlocksToday: gateBlocksToday, inProgressCount: evidenceCount, pendingCount: pendingCount }), !experienceMode && governanceState === "none" && pendingCount === 0 && deviationCount === 0 && stagnationCount === 0 && (stateReasonCode === "state_db_missing" ? (_jsx(ZeroStateDbMissing, {})) : (_jsxs(_Fragment, { children: [_jsx(ZeroStateHealthy, {}), _jsx("div", { className: "mt-4", children: _jsx(OnboardingGuide, {}) })] }))), governanceState === "in_progress" && inProgressSummary && (_jsx(InProgressGuide, { summary: inProgressSummary })), degradedSignals && degradedSignals.length > 0 && (_jsx(DegradedSummary, { signals: degradedSignals }))] })), _jsx(DailyThoughtCard, {}), experienceMode && experienceData !== null && experienceData.summary.primaryAttention === "all_clear" && pendingGroups.length === 0 && (_jsx("div", { className: "mt-4", children: _jsx(OnboardingGuide, {}) })), (ruleCodeErrorReason || ruleCodePending.length > 0 || ruleCodeAlerts.length > 0) && (_jsxs("section", { className: "mt-8", "aria-labelledby": "section-rulecode-owner", children: [_jsx(SectionTitle, { id: "section-rulecode-owner", children: t("pages.focus.ruleCodeOwnerQueue") }), ruleCodeErrorReason && (_jsxs("div", { className: "mb-3 rounded border border-amber/30 bg-panel p-4 text-[12px] text-ink-3", children: [_jsx("div", { className: "font-medium text-amber", children: t("pages.focus.ruleCodeUnavailable") }), _jsx("div", { className: "mt-1 font-mono", children: ruleCodeErrorReason }), _jsx("div", { className: "mt-1", children: t("pages.focus.ruleCodeUnavailableNextAction") })] })), _jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [ruleCodePending.length > 0 && (_jsxs(Link, { to: "/activation", className: "rounded border border-gov/30 bg-panel p-4 hover:border-gov", children: [_jsx("div", { className: "font-medium text-ink", children: t("pages.focus.ruleCodePending") }), _jsx("div", { className: "mt-1 text-[12px] text-ink-3", children: t("pages.focus.ruleCodePendingCount", { count: ruleCodePending.length }) })] })), ruleCodeAlerts.length > 0 && (_jsxs(Link, { to: "/activation", className: "rounded border border-danger/30 bg-panel p-4 hover:border-danger", children: [_jsx("div", { className: "font-medium text-danger", children: t("pages.focus.ruleCodeAlerts") }), _jsx("div", { className: "mt-1 text-[12px] text-ink-3", children: t("pages.focus.ruleCodeAlertCount", { count: ruleCodeAlerts.length }) })] }))] })] })), _jsxs("section", { className: "mt-8", "aria-labelledby": "section-pending", children: [_jsx(SectionTitle, { id: "section-pending", children: t("pages.focus.sectionPending") }), pendingGroups.length > 0 ? (_jsx("div", { className: "space-y-[14px]", children: pendingGroups.map((group) => (_jsx(PendingReviewCard, { group: group, onDecisionApplied: loadData }, group.principleId))) })) : (_jsx("div", { className: "text-ink-3 text-[13px] leading-relaxed py-3", children: approvalDataUnavailable
569
+ : "text-ink-4 border border-line bg-surface/80"}`, role: "status", children: t(`pages.focus.stateLabel.${governanceState}`) }) }), _jsx("div", { className: "text-ink-2 text-[13px] leading-relaxed mt-1", children: stateReason }), _jsxs("div", { className: "text-ink-4 text-[13px] leading-relaxed mt-1", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.nextActionLabel") }), " ", nextAction] })] })) : null, _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
570
+ ? t("pages.focus.ownerDecision.unavailable")
571
+ : `· ${ownerDecisionItems.length}` })] }), ownerDecisionError !== null && (_jsx("p", { className: "text-ink-4 text-[12.5px]", children: t("pages.focus.ownerDecision.loadError") })), ownerDecisionError === null && ownerDecisionItems.length === 0 && (_jsx("p", { className: "text-ink-4 text-[13px]", children: t("pages.focus.ownerDecision.empty") })), ownerDecisionItems.map((item) => (_jsx(OwnerDecisionCard, { item: item, onResolved: () => { void loadData(); } }, item.reviewKey)))] }), !experienceMode && (_jsxs(_Fragment, { children: [_jsx(ProseSummary, { pendingCount: pendingCount, deviationCount: deviationCount, stagnationCount: stagnationCount, evidenceCount: evidenceCount }), _jsx(FeedbackStratification, { gateBlocksToday: gateBlocksToday, inProgressCount: evidenceCount, pendingCount: pendingCount }), !experienceMode && governanceState === "none" && pendingCount === 0 && deviationCount === 0 && stagnationCount === 0 && (stateReasonCode === "state_db_missing" ? (_jsx(ZeroStateDbMissing, {})) : (_jsxs(_Fragment, { children: [_jsx(ZeroStateHealthy, {}), _jsx("div", { className: "mt-4", children: _jsx(OnboardingGuide, {}) })] }))), governanceState === "in_progress" && inProgressSummary && (_jsx(InProgressGuide, { summary: inProgressSummary })), degradedSignals && degradedSignals.length > 0 && (_jsx(DegradedSummary, { signals: degradedSignals }))] })), _jsx(DailyThoughtCard, {}), experienceMode && experienceData !== null && experienceData.summary.primaryAttention === "all_clear" && pendingGroups.length === 0 && (_jsx("div", { className: "mt-4", children: _jsx(OnboardingGuide, {}) })), (ruleCodeErrorReason || ruleCodePending.length > 0 || ruleCodeAlerts.length > 0) && (_jsxs("section", { className: "mt-8", "aria-labelledby": "section-rulecode-owner", children: [_jsx(SectionTitle, { id: "section-rulecode-owner", children: t("pages.focus.ruleCodeOwnerQueue") }), ruleCodeErrorReason && (_jsxs("div", { className: "mb-3 rounded border border-amber/30 bg-panel p-4 text-[12px] text-ink-3", children: [_jsx("div", { className: "font-medium text-amber", children: t("pages.focus.ruleCodeUnavailable") }), _jsx("div", { className: "mt-1 font-mono", children: ruleCodeErrorReason }), _jsx("div", { className: "mt-1", children: t("pages.focus.ruleCodeUnavailableNextAction") })] })), _jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [ruleCodePending.length > 0 && (_jsxs(Link, { to: "/activation", className: "rounded border border-gov/30 bg-panel p-4 hover:border-gov", children: [_jsx("div", { className: "font-medium text-ink", children: t("pages.focus.ruleCodePending") }), _jsx("div", { className: "mt-1 text-[12px] text-ink-3", children: t("pages.focus.ruleCodePendingCount", { count: ruleCodePending.length }) })] })), ruleCodeAlerts.length > 0 && (_jsxs(Link, { to: "/activation", className: "rounded border border-danger/30 bg-panel p-4 hover:border-danger", children: [_jsx("div", { className: "font-medium text-danger", children: t("pages.focus.ruleCodeAlerts") }), _jsx("div", { className: "mt-1 text-[12px] text-ink-3", children: t("pages.focus.ruleCodeAlertCount", { count: ruleCodeAlerts.length }) })] }))] })] })), _jsxs("section", { id: "approval-queue-section", className: "mt-8", "aria-labelledby": "section-pending", children: [_jsx(SectionTitle, { id: "section-pending", children: t("pages.focus.sectionPending") }), pendingGroups.length > 0 ? (_jsx("div", { className: "space-y-[14px]", children: pendingGroups.map((group) => (_jsx(PendingReviewCard, { group: group, onDecisionApplied: loadData }, group.principleId))) })) : (_jsx("div", { className: "text-ink-3 text-[13px] leading-relaxed py-3", children: approvalDataUnavailable
555
572
  ? (groupedErrorReason
556
573
  ? `${t("pages.focus.loadError")} (${groupedErrorReason})`
557
574
  : t("pages.focus.loadError"))
@@ -0,0 +1,6 @@
1
+ import type { OwnerDecisionItemData } from "../../utils/validators.js";
2
+ export interface OwnerDecisionCardProps {
3
+ item: OwnerDecisionItemData;
4
+ onResolved: () => void;
5
+ }
6
+ export declare function OwnerDecisionCard({ item, onResolved }: OwnerDecisionCardProps): import("react").JSX.Element;