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.
- package/console/dist/server/index.js +9 -4
- package/console/dist/server/models/OwnerDecisionConsoleModel.d.ts +5 -0
- package/console/dist/server/models/OwnerDecisionConsoleModel.js +92 -18
- package/console/dist/server/routes/governance.js +6 -1
- package/console/dist/server/routes/health.d.ts +4 -1
- package/console/dist/server/routes/health.js +3 -3
- package/console/dist/server/routes/owner-decisions.d.ts +3 -3
- package/console/dist/server/routes/owner-decisions.js +41 -3
- package/console/dist/ui/api.d.ts +5 -0
- package/console/dist/ui/i18n/en.json +22 -1
- package/console/dist/ui/i18n/zh-CN.json +22 -1
- package/console/dist/ui/pages/focus/FocusPage.js +17 -30
- package/console/dist/ui/pages/focus/OwnerDecisionCard.d.ts +2 -1
- package/console/dist/ui/pages/focus/OwnerDecisionCard.js +11 -6
- package/console/dist/ui/pages/settings/SettingsPage.js +3 -3
- package/console/dist/ui/utils/validators.d.ts +45 -1
- package/console/dist/ui/utils/validators.js +112 -4
- package/console/dist/web/assets/app.css +0 -9
- package/console/dist/web/assets/app.js +313 -449
- package/console/package.json +1 -1
- package/core/dist/runtime-v2/__tests__/adversarial-loop.test.js +22 -0
- package/core/dist/runtime-v2/__tests__/adversarial-loop.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/governance-experience.test.js +19 -0
- package/core/dist/runtime-v2/__tests__/governance-experience.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.js +243 -0
- package/core/dist/runtime-v2/__tests__/rollout-rule-candidate-contract.test.js.map +1 -0
- package/core/dist/runtime-v2/governance-experience-contract.d.ts +16 -16
- package/core/dist/runtime-v2/governance-experience-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/governance-experience-contract.js +4 -1
- package/core/dist/runtime-v2/governance-experience-contract.js.map +1 -1
- package/core/dist/runtime-v2/governance-experience.d.ts.map +1 -1
- package/core/dist/runtime-v2/governance-experience.js +30 -8
- package/core/dist/runtime-v2/governance-experience.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +3 -1
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +2 -1
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.js +114 -0
- package/core/dist/runtime-v2/internalization/__tests__/artificer-code-bearing.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js +256 -0
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-gate-authority.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.js +229 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-review.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +172 -1
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.js +187 -0
- package/core/dist/runtime-v2/internalization/__tests__/rule-activation-contract.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/artificer-code-bearing.d.ts +39 -0
- package/core/dist/runtime-v2/internalization/artificer-code-bearing.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/artificer-code-bearing.js +33 -0
- package/core/dist/runtime-v2/internalization/artificer-code-bearing.js.map +1 -0
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +11 -8
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +170 -31
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/index.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/index.js +1 -0
- package/core/dist/runtime-v2/internalization/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts +98 -0
- package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-decision-review.js +287 -0
- package/core/dist/runtime-v2/internalization/owner-decision-review.js.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts +11 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-resolution-service.js +117 -4
- package/core/dist/runtime-v2/internalization/owner-resolution-service.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts +4 -0
- package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +16 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.js +62 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +13 -0
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js +59 -11
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rule-activation-contract.d.ts +35 -0
- package/core/dist/runtime-v2/internalization/rule-activation-contract.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/rule-activation-contract.js +36 -0
- package/core/dist/runtime-v2/internalization/rule-activation-contract.js.map +1 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +6 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.js +8 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js +36 -0
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +19 -0
- package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-task-store.js +30 -1
- package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
- package/package.json +1 -1
- package/pd-cli/dist/commands/console.d.ts.map +1 -1
- package/pd-cli/dist/commands/console.js +34 -1
- package/pd-cli/dist/commands/console.js.map +1 -1
- package/pd-cli/dist/services/console-launcher.d.ts +8 -2
- package/pd-cli/dist/services/console-launcher.d.ts.map +1 -1
- package/pd-cli/dist/services/console-launcher.js +34 -4
- package/pd-cli/dist/services/console-launcher.js.map +1 -1
- package/plugin/dist/bundle.js +515 -505
- package/plugin/dist/governance-audit.js +132 -132
- package/plugin/dist/rulehost-evidence.js +148 -148
- 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
|
|
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
|
|
434
|
-
:
|
|
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,
|
|
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,
|
|
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
|
-
|
|
84
|
-
|
|
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
|
|
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
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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.
|
|
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,
|
|
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:
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
/**
|
|
17
|
+
/** Only present after both Console authentication and Owner identity verification. */
|
|
18
18
|
ownerIdentity: {
|
|
19
19
|
ownerId: string;
|
|
20
|
-
credentialId
|
|
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 (
|
|
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;
|
package/console/dist/ui/api.d.ts
CHANGED
|
@@ -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": "再修改一次",
|