create-principles-disciple 1.122.2 → 1.123.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 (114) hide show
  1. package/console/dist/server/index.js +9 -4
  2. package/console/dist/server/models/OwnerDecisionConsoleModel.d.ts +5 -0
  3. package/console/dist/server/models/OwnerDecisionConsoleModel.js +92 -18
  4. package/console/dist/server/routes/governance.js +6 -1
  5. package/console/dist/server/routes/health.d.ts +4 -1
  6. package/console/dist/server/routes/health.js +3 -3
  7. package/console/dist/server/routes/owner-decisions.d.ts +3 -3
  8. package/console/dist/server/routes/owner-decisions.js +41 -3
  9. package/console/dist/ui/api.d.ts +5 -0
  10. package/console/dist/ui/i18n/en.json +22 -1
  11. package/console/dist/ui/i18n/zh-CN.json +22 -1
  12. package/console/dist/ui/pages/focus/FocusPage.js +17 -30
  13. package/console/dist/ui/pages/focus/OwnerDecisionCard.d.ts +2 -1
  14. package/console/dist/ui/pages/focus/OwnerDecisionCard.js +11 -6
  15. package/console/dist/ui/pages/settings/SettingsPage.js +3 -3
  16. package/console/dist/ui/utils/validators.d.ts +45 -1
  17. package/console/dist/ui/utils/validators.js +112 -4
  18. package/console/dist/web/assets/app.css +0 -9
  19. package/console/dist/web/assets/app.js +313 -449
  20. package/console/package.json +1 -1
  21. package/core/dist/runtime-v2/__tests__/adversarial-loop.test.js +22 -0
  22. package/core/dist/runtime-v2/__tests__/adversarial-loop.test.js.map +1 -1
  23. package/core/dist/runtime-v2/__tests__/governance-experience.test.js +19 -0
  24. package/core/dist/runtime-v2/__tests__/governance-experience.test.js.map +1 -1
  25. package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.d.ts +2 -0
  26. package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.d.ts.map +1 -0
  27. package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.js +243 -0
  28. package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.js.map +1 -0
  29. package/core/dist/runtime-v2/governance-experience-contract.d.ts +16 -16
  30. package/core/dist/runtime-v2/governance-experience-contract.d.ts.map +1 -1
  31. package/core/dist/runtime-v2/governance-experience-contract.js +4 -1
  32. package/core/dist/runtime-v2/governance-experience-contract.js.map +1 -1
  33. package/core/dist/runtime-v2/governance-experience.d.ts.map +1 -1
  34. package/core/dist/runtime-v2/governance-experience.js +30 -8
  35. package/core/dist/runtime-v2/governance-experience.js.map +1 -1
  36. package/core/dist/runtime-v2/index.d.ts +3 -1
  37. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  38. package/core/dist/runtime-v2/index.js +2 -1
  39. package/core/dist/runtime-v2/index.js.map +1 -1
  40. package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.d.ts +2 -0
  41. package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.d.ts.map +1 -0
  42. package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.js +114 -0
  43. package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.js.map +1 -0
  44. package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.d.ts +2 -0
  45. package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.d.ts.map +1 -0
  46. package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js +256 -0
  47. package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js.map +1 -0
  48. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.d.ts +2 -0
  49. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.d.ts.map +1 -0
  50. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.js +229 -0
  51. package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.js.map +1 -0
  52. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +172 -1
  53. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -1
  54. package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.d.ts +2 -0
  55. package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.d.ts.map +1 -0
  56. package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.js +187 -0
  57. package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.js.map +1 -0
  58. package/core/dist/runtime-v2/internalization/artificer-code-bearing.d.ts +39 -0
  59. package/core/dist/runtime-v2/internalization/artificer-code-bearing.d.ts.map +1 -0
  60. package/core/dist/runtime-v2/internalization/artificer-code-bearing.js +33 -0
  61. package/core/dist/runtime-v2/internalization/artificer-code-bearing.js.map +1 -0
  62. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +11 -8
  63. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  64. package/core/dist/runtime-v2/internalization/evaluator-runner.js +170 -31
  65. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  66. package/core/dist/runtime-v2/internalization/index.d.ts +2 -0
  67. package/core/dist/runtime-v2/internalization/index.d.ts.map +1 -1
  68. package/core/dist/runtime-v2/internalization/index.js +1 -0
  69. package/core/dist/runtime-v2/internalization/index.js.map +1 -1
  70. package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts +98 -0
  71. package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts.map +1 -0
  72. package/core/dist/runtime-v2/internalization/owner-decision-review.js +287 -0
  73. package/core/dist/runtime-v2/internalization/owner-decision-review.js.map +1 -0
  74. package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts +11 -0
  75. package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts.map +1 -1
  76. package/core/dist/runtime-v2/internalization/owner-resolution-service.js +117 -4
  77. package/core/dist/runtime-v2/internalization/owner-resolution-service.js.map +1 -1
  78. package/core/dist/runtime-v2/internalization/owner-review.d.ts +4 -0
  79. package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -1
  80. package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -1
  81. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +16 -0
  82. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
  83. package/core/dist/runtime-v2/internalization/pitask-metadata.js +62 -0
  84. package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
  85. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +13 -0
  86. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
  87. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js +59 -11
  88. package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
  89. package/core/dist/runtime-v2/internalization/rule-activation-contract.d.ts +35 -0
  90. package/core/dist/runtime-v2/internalization/rule-activation-contract.d.ts.map +1 -0
  91. package/core/dist/runtime-v2/internalization/rule-activation-contract.js +36 -0
  92. package/core/dist/runtime-v2/internalization/rule-activation-contract.js.map +1 -0
  93. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +6 -0
  94. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
  95. package/core/dist/runtime-v2/store/runtime-state-manager.js +8 -0
  96. package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
  97. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js +36 -0
  98. package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js.map +1 -1
  99. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +19 -0
  100. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  101. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +30 -1
  102. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  103. package/package.json +1 -1
  104. package/pd-cli/dist/commands/console.d.ts.map +1 -1
  105. package/pd-cli/dist/commands/console.js +34 -1
  106. package/pd-cli/dist/commands/console.js.map +1 -1
  107. package/pd-cli/dist/services/console-launcher.d.ts +8 -2
  108. package/pd-cli/dist/services/console-launcher.d.ts.map +1 -1
  109. package/pd-cli/dist/services/console-launcher.js +34 -4
  110. package/pd-cli/dist/services/console-launcher.js.map +1 -1
  111. package/plugin/dist/bundle.js +515 -505
  112. package/plugin/dist/governance-audit.js +132 -132
  113. package/plugin/dist/rulehost-evidence.js +148 -148
  114. package/plugin/dist/service/workspace-telemetry-sink.d.ts +14 -0
@@ -425,13 +425,15 @@ function handleRequest(services) {
425
425
  if (urlPath === '/api/v1/governance/owner-decisions' || urlPath.startsWith('/api/v1/governance/owner-decisions/')) {
426
426
  const subPath = urlPath.slice('/api/v1/governance/owner-decisions'.length);
427
427
  const odIdentity = resolveOwnerIdentity(process.env, defaultOwnerIdentityHomeDir());
428
- const authEnabledForOwner = services.authConfig.isEnabled() && odIdentity.ownerId && odIdentity.credentialId;
428
+ const { ownerId, credentialId } = odIdentity;
429
+ const authEnabledForOwner = services.authConfig.isEnabled()
430
+ && ownerId !== null && credentialId !== null;
429
431
  asyncHandler(() => handleOwnerDecisionsRoute(req, res, {
430
432
  workspaceDir: services.workspaceDir,
431
433
  subPath,
432
434
  ownerIdentity: authEnabledForOwner
433
- ? { ownerId: odIdentity.ownerId ?? 'console_operator', credentialId: odIdentity.credentialId ?? undefined }
434
- : { ownerId: 'console_operator' },
435
+ ? { ownerId, credentialId }
436
+ : null,
435
437
  }))(req, res);
436
438
  return;
437
439
  }
@@ -461,7 +463,10 @@ function handleRequest(services) {
461
463
  }
462
464
  // GET /api/health
463
465
  if (urlPath === '/api/health') {
464
- asyncHandler(() => handleHealthRoute(req, res, services.workspaceDir))(req, res);
466
+ asyncHandler(() => handleHealthRoute(req, res, {
467
+ workspaceDir: services.workspaceDir,
468
+ authenticationMode: services.authConfig.isEnabled() ? 'authenticated' : 'no_auth',
469
+ }))(req, res);
465
470
  return;
466
471
  }
467
472
  // 404 fallback for API routes
@@ -1,3 +1,4 @@
1
+ import type { OwnerDecisionReviewSnapshot } from '@principles/core/runtime-v2';
1
2
  export type OwnerDecisionItemKind = 'evaluator_review' | 'rollout_review' | 'activation_approval' | 'rulecode_decision';
2
3
  export interface OwnerDecisionItem {
3
4
  readonly reviewKey: string;
@@ -16,6 +17,10 @@ export interface OwnerDecisionItem {
16
17
  readonly expectedSourceRunId: string;
17
18
  readonly expectedSourceArtifactId: string;
18
19
  readonly expectedSourceArtifactHash: string;
20
+ readonly expectedEvidenceDigest?: string;
21
+ readonly review?: OwnerDecisionReviewSnapshot;
22
+ /** Visible recovery state: the decision is still required, but evidence is unsafe to act on. */
23
+ readonly evidenceUnavailableReason?: string;
19
24
  readonly createdAt: string;
20
25
  }
21
26
  export interface OwnerDecisionListResult {
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import * as fs from 'node:fs';
13
13
  import * as path from 'node:path';
14
- import { SqliteConnection, SqlitePIArtifactStore, SqliteApprovalQueueStore, collectOwnerDecisionFacts, deriveOwnerDecisionCapability, } from '@principles/core/runtime-v2';
14
+ import { SqliteConnection, SqlitePIArtifactStore, SqliteApprovalQueueStore, collectOwnerDecisionFacts, deriveOwnerDecisionCapability, buildOwnerDecisionReview, } from '@principles/core/runtime-v2';
15
15
  const GOVERNANCE_TASK_KINDS = new Set(['evaluator', 'rollout_reviewer']);
16
16
  /** tasks.status 合法值(PDTaskStatus)运行时守卫。 */
17
17
  const TASK_STATUSES = new Set(['pending', 'leased', 'succeeded', 'retry_wait', 'failed', 'needs_human_review']);
@@ -50,9 +50,8 @@ function makeRowReader(db) {
50
50
  return row ? rowToTaskRecord(row) : null;
51
51
  };
52
52
  }
53
- /** 行 → TaskRecord 的窄映射(tasks 表全部列;diagnostic_json 键名转换)。 */
54
53
  /** 单任务 capability → 决策条目(不 eligible → null,INV-01)。 */
55
- async function deriveTaskDecisionItem(task, artifactStore, readTaskRow) {
54
+ async function deriveTaskDecisionItem(task, dependencies) {
56
55
  if (!GOVERNANCE_TASK_KINDS.has(task.taskKind))
57
56
  return null;
58
57
  const factStore = {
@@ -60,48 +59,119 @@ async function deriveTaskDecisionItem(task, artifactStore, readTaskRow) {
60
59
  if (taskId === task.taskId)
61
60
  return task;
62
61
  try {
63
- return readTaskRow(taskId);
62
+ return dependencies.readTaskRow(taskId);
64
63
  }
65
64
  catch {
66
65
  return null;
67
66
  }
68
67
  },
69
68
  listArtifactsBySourceTask: async (taskId) => {
70
- const list = await artifactStore.listBySourceTaskId(taskId).catch(() => []);
69
+ const list = await dependencies.artifactStore.listBySourceTaskId(taskId).catch(() => []);
71
70
  return list.map((a) => ({
72
71
  artifactId: a.artifactId,
73
72
  artifactKind: a.artifactKind,
74
73
  validationStatus: a.validationStatus,
75
74
  contentJson: a.contentJson,
75
+ sourceTaskId: a.sourceTaskId,
76
+ lineageArtifactIds: a.lineageArtifactIds,
77
+ createdAt: a.createdAt,
76
78
  }));
77
79
  },
80
+ getArtifactById: async (artifactId) => {
81
+ const artifact = await dependencies.artifactStore.getArtifactById(artifactId).catch(() => null);
82
+ return artifact ? {
83
+ artifactId: artifact.artifactId,
84
+ artifactKind: artifact.artifactKind,
85
+ validationStatus: artifact.validationStatus,
86
+ contentJson: artifact.contentJson,
87
+ sourceTaskId: artifact.sourceTaskId,
88
+ lineageArtifactIds: artifact.lineageArtifactIds,
89
+ createdAt: artifact.createdAt,
90
+ } : null;
91
+ },
78
92
  };
79
93
  const facts = await collectOwnerDecisionFacts(factStore, task.taskId);
80
94
  if (!facts)
81
95
  return null;
82
96
  const capability = deriveOwnerDecisionCapability(facts);
83
- if (!capability.eligible || !capability.reviewKey)
84
- return null;
97
+ const evidenceBlockers = capability.blockers.filter((blocker) => blocker === 'decision_artifact_missing' || blocker === 'lineage_unresolvable');
98
+ const kind = task.taskKind === 'rollout_reviewer' ? 'rollout_review' : 'evaluator_review';
99
+ if (!capability.eligible || !capability.reviewKey) {
100
+ if (evidenceBlockers.length === 0)
101
+ return null;
102
+ return {
103
+ reviewKey: `unavailable:${task.taskId}`,
104
+ kind,
105
+ taskId: task.taskId,
106
+ title: task.taskKind === 'rollout_reviewer'
107
+ ? 'Rollout 评审仍需要你的判断'
108
+ : '自动改进仍需要你的判断',
109
+ summary: '决策证据当前不完整,PD 已停止提供裁决动作以避免你批准错误版本。',
110
+ reasonCode: capability.reasonCode,
111
+ legacy: capability.legacy,
112
+ allowedActions: [],
113
+ expectedRevisionEpoch: facts.task.revisionCount ?? 0,
114
+ expectedSourceRunId: facts.task.humanReviewContext?.sourceRunId
115
+ ?? facts.task.completionIntent?.sourceRunId ?? '',
116
+ expectedSourceArtifactId: '',
117
+ expectedSourceArtifactHash: '',
118
+ evidenceUnavailableReason: evidenceBlockers.join(','),
119
+ createdAt: task.updatedAt,
120
+ };
121
+ }
122
+ const review = await buildOwnerDecisionReview(factStore, task.taskId);
123
+ if (!review) {
124
+ return {
125
+ reviewKey: `unavailable:${task.taskId}`,
126
+ kind,
127
+ taskId: task.taskId,
128
+ title: '自动改进仍需要你的判断',
129
+ summary: '决策证据无法安全读取,PD 已停止提供裁决动作。',
130
+ reasonCode: capability.reasonCode,
131
+ legacy: capability.legacy,
132
+ allowedActions: [],
133
+ expectedRevisionEpoch: facts.task.revisionCount ?? 0,
134
+ expectedSourceRunId: facts.task.humanReviewContext?.sourceRunId
135
+ ?? facts.task.completionIntent?.sourceRunId ?? '',
136
+ expectedSourceArtifactId: facts.decisionArtifact?.artifactId ?? '',
137
+ expectedSourceArtifactHash: facts.decisionArtifact?.contentHash ?? '',
138
+ evidenceUnavailableReason: 'owner_review_evidence_unavailable',
139
+ createdAt: task.updatedAt,
140
+ };
141
+ }
85
142
  const sourceRunId = facts.task.humanReviewContext?.sourceRunId
86
143
  ?? facts.task.completionIntent?.sourceRunId ?? '';
87
144
  const artifact = facts.decisionArtifact;
88
- const kind = task.taskKind === 'rollout_reviewer' ? 'rollout_review' : 'evaluator_review';
145
+ const title = review.brief.kind === 'evaluator'
146
+ ? review.brief.principle.title
147
+ ?? review.brief.principle.statement
148
+ ?? review.brief.implementation.summary
149
+ ?? '自动改进需要你的判断'
150
+ : review.brief.summary ?? 'Rollout 评审需要你的判断';
151
+ const summary = review.brief.kind === 'evaluator'
152
+ ? review.brief.implementation.summary
153
+ ?? (review.brief.concerns.length > 0
154
+ ? `仍有 ${String(review.brief.concerns.length)} 项自动评审异议。`
155
+ : '查看当前实现、证据与动作后果。')
156
+ : review.brief.summary ?? '查看 rollout 证据与动作后果。';
89
157
  return {
90
158
  reviewKey: capability.reviewKey,
91
159
  kind,
92
160
  taskId: task.taskId,
93
- title: kind === 'rollout_review'
94
- ? 'Rollout 评审无法收敛'
95
- : '自动改进已达到本轮上限',
96
- summary: `机器建议继续修改(${capability.reasonCode}${capability.legacy ? ',由历史数据安全推断' : ''})。需要你的判断:接受当前版本、再修改一次,或拒绝。`,
97
- machineRecommendation: 'needs_revision(继续修改)',
161
+ title,
162
+ summary,
163
+ ...(review.brief.kind === 'evaluator' && review.brief.score !== undefined
164
+ ? { score: review.brief.score }
165
+ : {}),
98
166
  reasonCode: capability.reasonCode,
99
167
  legacy: capability.legacy,
100
- allowedActions: [...capability.allowedActions],
168
+ allowedActions: [...review.capability.finalOfferedActions],
101
169
  expectedRevisionEpoch: facts.task.revisionCount ?? 0,
102
170
  expectedSourceRunId: sourceRunId,
103
171
  expectedSourceArtifactId: artifact?.artifactId ?? '',
104
172
  expectedSourceArtifactHash: artifact?.contentHash ?? '',
173
+ expectedEvidenceDigest: review.evidence.digest,
174
+ review,
105
175
  createdAt: task.updatedAt,
106
176
  };
107
177
  }
@@ -136,7 +206,10 @@ export class OwnerDecisionConsoleModel {
136
206
  const task = rowToTaskRecord(row);
137
207
  if (!task)
138
208
  continue;
139
- const item = await deriveTaskDecisionItem(task, artifactStore, makeRowReader(db));
209
+ const item = await deriveTaskDecisionItem(task, {
210
+ artifactStore,
211
+ readTaskRow: makeRowReader(db),
212
+ });
140
213
  if (item)
141
214
  items.push(item);
142
215
  }
@@ -166,10 +239,11 @@ export class OwnerDecisionConsoleModel {
166
239
  }
167
240
  // ── 3. RuleCode shadow 待决(promote / reject — 独立 authority,动作走既有 API)──
168
241
  try {
169
- const shadowRows = db.prepare(`SELECT activation_id, channel, activated_at FROM activations
242
+ const shadowRows = db.prepare(`SELECT activation_id, artifact_id, channel, activated_at FROM activations
170
243
  WHERE action = 'code_tool_hook_shadow_activate'
171
244
  AND promoted_at IS NULL AND deactivated_at IS NULL`).all().filter(isRecordRow);
172
245
  for (const row of shadowRows) {
246
+ const artifactId = typeof row.artifact_id === 'string' ? row.artifact_id : '';
173
247
  items.push({
174
248
  reviewKey: `rulecode:${String(row.activation_id ?? '')}`,
175
249
  kind: 'rulecode_decision',
@@ -181,7 +255,7 @@ export class OwnerDecisionConsoleModel {
181
255
  allowedActions: ['promote', 'reject_after_shadow'],
182
256
  expectedRevisionEpoch: 0,
183
257
  expectedSourceRunId: '',
184
- expectedSourceArtifactId: String(row.activation_id ?? ''),
258
+ expectedSourceArtifactId: artifactId,
185
259
  expectedSourceArtifactHash: '',
186
260
  createdAt: typeof row.activated_at === 'string' ? row.activated_at : '',
187
261
  });
@@ -220,7 +294,7 @@ export class OwnerDecisionConsoleModel {
220
294
  const task = rowToTaskRecord(row);
221
295
  if (!task)
222
296
  continue;
223
- const item = await deriveTaskDecisionItem(task, artifactStore, makeRowReader(db));
297
+ const item = await deriveTaskDecisionItem(task, { artifactStore, readTaskRow: makeRowReader(db) });
224
298
  if (item)
225
299
  ids.add(task.taskId);
226
300
  }
@@ -27,9 +27,14 @@ function getExperienceCollector(workspaceDir) {
27
27
  */
28
28
  export function resolveOwnerConfigSnapshot(authConfig, identity) {
29
29
  const authEnabled = authConfig.isEnabled();
30
+ const ownerIdentityConfiguration = identity.source === 'invalid_env' || identity.error !== undefined
31
+ ? 'invalid'
32
+ : (identity.source === 'env' || identity.source === 'file') && identity.ownerId && identity.credentialId
33
+ ? 'configured'
34
+ : 'missing';
30
35
  return {
31
36
  authenticationMode: authEnabled ? 'authenticated' : 'no_auth',
32
- ownerIdentityConfiguration: authEnabled && identity.ownerId && identity.credentialId ? 'configured' : 'missing',
37
+ ownerIdentityConfiguration,
33
38
  };
34
39
  }
35
40
  /**
@@ -1,3 +1,6 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- export declare function handleHealthRoute(req: IncomingMessage, res: ServerResponse, workspaceDir: string): Promise<void>;
2
+ export declare function handleHealthRoute(req: IncomingMessage, res: ServerResponse, options: {
3
+ workspaceDir: string;
4
+ authenticationMode: 'authenticated' | 'no_auth';
5
+ }): Promise<void>;
3
6
  export declare function disposeHealthModels(): void;
@@ -11,15 +11,15 @@ function getModel(workspaceDir) {
11
11
  models.set(workspaceDir, { model, cachedAt: Date.now() });
12
12
  return model;
13
13
  }
14
- export async function handleHealthRoute(req, res, workspaceDir) {
14
+ export async function handleHealthRoute(req, res, options) {
15
15
  if (req.method !== 'GET') {
16
16
  sendError(res, 405, 'method_not_allowed', 'Only GET method is allowed');
17
17
  return;
18
18
  }
19
- const model = getModel(workspaceDir);
19
+ const model = getModel(options.workspaceDir);
20
20
  try {
21
21
  const health = await model.checkSystemHealth();
22
- sendSuccess(res, health);
22
+ sendSuccess(res, { ...health, authenticationMode: options.authenticationMode });
23
23
  }
24
24
  catch (err) {
25
25
  sendError(res, 500, 'health_check_error', err.message);
@@ -14,11 +14,11 @@
14
14
  import type { IncomingMessage, ServerResponse } from 'node:http';
15
15
  export interface OwnerDecisionRouteContext {
16
16
  workspaceDir: string;
17
- /** server-side auth context 推导的身份(SPEC §29 不信任 body) */
17
+ /** Only present after both Console authentication and Owner identity verification. */
18
18
  ownerIdentity: {
19
19
  ownerId: string;
20
- credentialId?: string;
21
- };
20
+ credentialId: string;
21
+ } | null;
22
22
  }
23
23
  export declare function handleOwnerDecisionsRoute(req: IncomingMessage, res: ServerResponse, ctx: OwnerDecisionRouteContext & {
24
24
  subPath: string;
@@ -12,16 +12,19 @@ function getModel(workspaceDir) {
12
12
  return model;
13
13
  }
14
14
  const RESOLVE_ACTIONS = new Set(['accept_current', 'revise_once', 'reject_current']);
15
+ function isRecord(value) {
16
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
17
+ }
15
18
  /** rc-1/rc-2/rc-3: body 按未知值逐字段校验,非法即 400 fail-loud。 */
16
19
  function parseResolveBody(raw) {
17
- if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
20
+ if (!isRecord(raw)) {
18
21
  return { ok: false, error: 'Request body must be a JSON object.' };
19
22
  }
20
23
  const b = raw;
21
24
  if (typeof b.action !== 'string' || !RESOLVE_ACTIONS.has(b.action)) {
22
25
  return { ok: false, error: `action must be one of accept_current/revise_once/reject_current, got ${String(b.action)}` };
23
26
  }
24
- for (const field of ['reviewKey', 'expectedSourceRunId', 'expectedSourceArtifactId', 'expectedSourceArtifactHash']) {
27
+ for (const field of ['reviewKey', 'expectedSourceRunId', 'expectedSourceArtifactId', 'expectedSourceArtifactHash', 'expectedEvidenceDigest']) {
25
28
  const v = b[field];
26
29
  if (typeof v !== 'string' || v.trim() === '') {
27
30
  return { ok: false, error: `${field} must be a non-empty string.` };
@@ -34,12 +37,34 @@ function parseResolveBody(raw) {
34
37
  && (typeof b.ownerInstruction !== 'string' || b.ownerInstruction.length > 600)) {
35
38
  return { ok: false, error: 'ownerInstruction must be a string of at most 600 characters (or null).' };
36
39
  }
40
+ let acknowledgement;
41
+ if (b.acknowledgement !== undefined) {
42
+ if (!isRecord(b.acknowledgement)) {
43
+ return { ok: false, error: 'acknowledgement must be an object.' };
44
+ }
45
+ const rawAcknowledgement = b.acknowledgement;
46
+ if (rawAcknowledgement.kind !== 'partial_evidence' || rawAcknowledgement.acknowledged !== true) {
47
+ return { ok: false, error: 'acknowledgement must explicitly acknowledge partial_evidence.' };
48
+ }
49
+ if (rawAcknowledgement.note !== undefined
50
+ && (typeof rawAcknowledgement.note !== 'string' || rawAcknowledgement.note.length > 600)) {
51
+ return { ok: false, error: 'acknowledgement.note must be at most 600 characters.' };
52
+ }
53
+ const note = typeof rawAcknowledgement.note === 'string' ? rawAcknowledgement.note.trim() : '';
54
+ acknowledgement = {
55
+ kind: 'partial_evidence',
56
+ acknowledged: true,
57
+ ...(note !== '' ? { note } : {}),
58
+ };
59
+ }
37
60
  const { reviewKey } = b;
38
61
  const { expectedSourceRunId } = b;
39
62
  const { expectedSourceArtifactId } = b;
40
63
  const { expectedSourceArtifactHash } = b;
64
+ const { expectedEvidenceDigest } = b;
41
65
  if (typeof reviewKey !== 'string' || typeof expectedSourceRunId !== 'string'
42
- || typeof expectedSourceArtifactId !== 'string' || typeof expectedSourceArtifactHash !== 'string') {
66
+ || typeof expectedSourceArtifactId !== 'string' || typeof expectedSourceArtifactHash !== 'string'
67
+ || typeof expectedEvidenceDigest !== 'string') {
43
68
  return { ok: false, error: 'unreachable: string fields validated above' };
44
69
  }
45
70
  return {
@@ -51,6 +76,8 @@ function parseResolveBody(raw) {
51
76
  expectedSourceRunId,
52
77
  expectedSourceArtifactId,
53
78
  expectedSourceArtifactHash,
79
+ expectedEvidenceDigest,
80
+ ...(acknowledgement !== undefined ? { acknowledgement } : {}),
54
81
  ...(typeof b.ownerInstruction === 'string' ? { ownerInstruction: b.ownerInstruction } : {}),
55
82
  },
56
83
  };
@@ -92,6 +119,11 @@ function sendOutcome(res, outcome) {
92
119
  nextAction: 'Refresh the governance focus and decide again on the current version.',
93
120
  });
94
121
  return;
122
+ case 'evidence_acknowledgement_required':
123
+ sendError(res, 409, 'evidence_acknowledgement_required', 'Accepting partial evidence requires explicit acknowledgement.', {
124
+ nextAction: 'Review the missing evidence, acknowledge it explicitly, then submit again.',
125
+ });
126
+ return;
95
127
  case 'already_resolved':
96
128
  sendError(res, 409, 'already_resolved', `This decision was already resolved with action '${outcome.existingAction}'.`, {
97
129
  nextAction: 'Refresh the governance focus.',
@@ -149,6 +181,12 @@ export async function handleOwnerDecisionsRoute(req, res, ctx) {
149
181
  sendMethodNotAllowed(res);
150
182
  return;
151
183
  }
184
+ if (!ownerIdentity) {
185
+ sendError(res, 403, 'owner_authentication_required', 'Owner decisions require an authenticated Console and a configured Owner identity.', {
186
+ nextAction: 'Configure the Console token and Owner identity, then retry from Governance Focus.',
187
+ });
188
+ return;
189
+ }
152
190
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- match group is always present
153
191
  const taskId = decodeURIComponent(resolveMatch[1]);
154
192
  let body;
@@ -157,6 +157,11 @@ declare function resolveOwnerDecision(taskId: string, body: {
157
157
  expectedSourceRunId: string;
158
158
  expectedSourceArtifactId: string;
159
159
  expectedSourceArtifactHash: string;
160
+ expectedEvidenceDigest: string;
161
+ acknowledgement?: {
162
+ kind: 'partial_evidence';
163
+ acknowledged: true;
164
+ };
160
165
  ownerInstruction?: string | null;
161
166
  }): Promise<ApiResponse<OwnerResolutionResultData>>;
162
167
  declare function fetchApprovalsGrouped(): Promise<ApiResponse<ApprovalsGroupedData>>;
@@ -147,6 +147,8 @@
147
147
  },
148
148
  "reason": {
149
149
  "ownerIdentityMissing": "The Owner identity is not configured. RuleCode governance decisions are blocked until PD_OWNER_ID and PD_OWNER_CREDENTIAL_ID are set and the console token auth is enabled.",
150
+ "ownerIdentityInvalid": "The Owner identity configuration is invalid, so governance actions are safely blocked.",
151
+ "ownerAuthenticationMissing": "The identity is registered, but Console token authentication is not enabled.",
150
152
  "approvalPending": "Principle approvals are waiting for your decision.",
151
153
  "rulecodeOwnerDecision": "RuleCode shadow activations are waiting for your promotion decision.",
152
154
  "noPendingDecision": "No principle approval is waiting right now.",
@@ -161,6 +163,7 @@
161
163
  },
162
164
  "next": {
163
165
  "configureOwner": "Configure PD_OWNER_ID and PD_OWNER_CREDENTIAL_ID, then enable console token auth.",
166
+ "authenticateConsole": "Enable Console token authentication, then retry.",
164
167
  "reviewApprovals": "Review the pending decisions below.",
165
168
  "inspectRecovery": "Open Failed Tasks and recover the stalled work.",
166
169
  "inspectSources": "Inspect state.db and the principle ledger, then restart the console.",
@@ -178,6 +181,12 @@
178
181
  "copied": "Copied",
179
182
  "docLink": "Configuration guide and verification →"
180
183
  },
184
+ "blocker": {
185
+ "identityMissing": "Register the Owner identity to enable governance actions.",
186
+ "identityInvalid": "The Owner identity configuration is invalid; fix it in Settings.",
187
+ "authenticationMissing": "Identity registered; enable Console token authentication to use governance actions.",
188
+ "goSettings": "Go to Settings"
189
+ },
181
190
  "activity": {
182
191
  "needsDecision": "{{count}} decision(s) waiting",
183
192
  "needsRecovery": "{{count}} item(s) need recovery",
@@ -187,7 +196,8 @@
187
196
  "readiness": {
188
197
  "identity": {
189
198
  "configured": "Owner identity: configured",
190
- "missing": "Owner identity: missing"
199
+ "missing": "Owner identity: missing",
200
+ "invalid": "Owner identity: invalid"
191
201
  },
192
202
  "principleApproval": "Principle approvals: operator entry",
193
203
  "rulecode": {
@@ -262,6 +272,8 @@
262
272
  "zeroStateDbMissing": "PD has not run in this workspace yet. Once the state database is initialized, governance data will appear here.",
263
273
  "evidenceInProgress": "{{count}} behavior evidence item(s) recorded, being diagnosed and processed.",
264
274
  "ownerDecision": {
275
+ "evidenceUnavailable": "This decision still needs you, but its evidence chain is incomplete. PD has paused all decision actions so you cannot approve the wrong version.",
276
+ "goRecoverCta": "View recovery details",
265
277
  "sectionTitle": "Needs your decision",
266
278
  "empty": "Nothing needs your decision right now. Problems the machine can solve on its own will not disturb you.",
267
279
  "unavailable": "(unavailable)",
@@ -272,6 +284,15 @@
272
284
  "kindRulecode": "RuleCode decision",
273
285
  "legacyBadge": "Inferred from history",
274
286
  "machineRecommendationLabel": "Machine recommendation",
287
+ "principleLabel": "Principle",
288
+ "implementationLabel": "Implementation",
289
+ "concernLabel": "Primary concern",
290
+ "partialEvidenceAcknowledgement": "I understand the evidence is incomplete and still want to accept this version.",
291
+ "evidence": {
292
+ "complete": "Evidence complete",
293
+ "partial": "Evidence partial",
294
+ "insufficient": "Evidence insufficient"
295
+ },
275
296
  "rolloutSafetyNote": "Accept this review and continue. High-risk rules still require an independent deployment approval.",
276
297
  "acceptCurrent": "Accept current version",
277
298
  "reviseOnce": "Revise once more",
@@ -147,6 +147,8 @@
147
147
  },
148
148
  "reason": {
149
149
  "ownerIdentityMissing": "尚未配置拥有者身份。RuleCode 治理决策入口被阻塞,需要设置 PD_OWNER_ID 与 PD_OWNER_CREDENTIAL_ID 并启用控制台令牌认证。",
150
+ "ownerIdentityInvalid": "拥有者身份配置无效,治理动作已安全阻止。",
151
+ "ownerAuthenticationMissing": "身份已登记,但控制台尚未启用令牌认证。",
150
152
  "approvalPending": "有原则审批正在等待你的决策。",
151
153
  "rulecodeOwnerDecision": "有 RuleCode 影子激活正在等待你的晋升决策。",
152
154
  "noPendingDecision": "当前没有等待决策的原则审批。",
@@ -161,6 +163,7 @@
161
163
  },
162
164
  "next": {
163
165
  "configureOwner": "配置 PD_OWNER_ID 与 PD_OWNER_CREDENTIAL_ID,并启用控制台令牌认证。",
166
+ "authenticateConsole": "启用控制台令牌认证后重试。",
164
167
  "reviewApprovals": "在下方处理待决策事项。",
165
168
  "inspectRecovery": "打开失败任务页面并恢复停滞的工作。",
166
169
  "inspectSources": "检查 state.db 与原则账本,然后重启控制台。",
@@ -178,6 +181,12 @@
178
181
  "copied": "已复制",
179
182
  "docLink": "查看配置说明与验证方法 →"
180
183
  },
184
+ "blocker": {
185
+ "identityMissing": "登记拥有者身份后即可执行治理动作。",
186
+ "identityInvalid": "拥有者身份配置无效;请在设置中修正。",
187
+ "authenticationMissing": "身份已登记;启用控制台令牌认证后治理动作即可执行。",
188
+ "goSettings": "前往设置"
189
+ },
181
190
  "activity": {
182
191
  "needsDecision": "{{count}} 项决策待处理",
183
192
  "needsRecovery": "{{count}} 项需要恢复",
@@ -187,7 +196,8 @@
187
196
  "readiness": {
188
197
  "identity": {
189
198
  "configured": "拥有者身份:已配置",
190
- "missing": "拥有者身份:未配置"
199
+ "missing": "拥有者身份:未配置",
200
+ "invalid": "拥有者身份:配置无效"
191
201
  },
192
202
  "principleApproval": "原则审批:操作员入口",
193
203
  "rulecode": {
@@ -262,6 +272,8 @@
262
272
  "zeroStateDbMissing": "PD 尚未在此工作空间中运行。状态数据库初始化后,治理数据将在这里呈现。",
263
273
  "evidenceInProgress": "{{count}} 条行为证据已记录,正在诊断和处理中。",
264
274
  "ownerDecision": {
275
+ "evidenceUnavailable": "这项裁决仍在等待你,但证据链目前不完整。PD 已暂停所有裁决动作,避免你在看不到正确版本时做决定。",
276
+ "goRecoverCta": "查看恢复详情",
265
277
  "sectionTitle": "需要你决定",
266
278
  "empty": "现在没有需要你决定的事项。机器能自己解决的问题不会打扰你。",
267
279
  "unavailable": "(暂不可用)",
@@ -272,6 +284,15 @@
272
284
  "kindRulecode": "RuleCode 决策",
273
285
  "legacyBadge": "历史数据推断",
274
286
  "machineRecommendationLabel": "机器建议",
287
+ "principleLabel": "原则",
288
+ "implementationLabel": "实现",
289
+ "concernLabel": "主要顾虑",
290
+ "partialEvidenceAcknowledgement": "我知道当前证据不完整,仍要接受当前版本。",
291
+ "evidence": {
292
+ "complete": "证据完整",
293
+ "partial": "证据不完整",
294
+ "insufficient": "证据不足"
295
+ },
275
296
  "rolloutSafetyNote": "接受当前评审并继续。高风险规则仍需经过独立部署审批。",
276
297
  "acceptCurrent": "接受当前版本",
277
298
  "reviseOnce": "再修改一次",