create-principles-disciple 1.121.10 → 1.122.1
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 +17 -0
- package/console/dist/server/models/OwnerDecisionConsoleModel.d.ts +39 -0
- package/console/dist/server/models/OwnerDecisionConsoleModel.js +233 -0
- package/console/dist/server/models/PrincipleClassifier.d.ts +3 -3
- package/console/dist/server/models/PrincipleClassifier.js +12 -5
- package/console/dist/server/models/PrinciplesConsoleModel.d.ts +1 -1
- package/console/dist/server/models/PrinciplesConsoleModel.js +2 -2
- package/console/dist/server/routes/failed-tasks.js +40 -2
- package/console/dist/server/routes/owner-decisions.d.ts +25 -0
- package/console/dist/server/routes/owner-decisions.js +186 -0
- package/console/dist/server/routes/principles.js +15 -7
- package/console/dist/ui/api.d.ts +12 -1
- package/console/dist/ui/api.js +9 -2
- package/console/dist/ui/components/notifications/NotificationProvider.js +8 -2
- package/console/dist/ui/i18n/en.json +33 -1
- package/console/dist/ui/i18n/zh-CN.json +33 -1
- package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +5 -1
- package/console/dist/ui/pages/focus/FocusPage.js +19 -2
- package/console/dist/ui/pages/focus/OwnerDecisionCard.d.ts +6 -0
- package/console/dist/ui/pages/focus/OwnerDecisionCard.js +86 -0
- package/console/dist/ui/pages/principles/PrinciplesPage.js +11 -5
- package/console/dist/ui/utils/validators.d.ts +37 -0
- package/console/dist/ui/utils/validators.js +123 -0
- package/console/dist/web/assets/app.css +11 -0
- package/console/dist/web/assets/app.js +2740 -2350
- package/core/dist/runtime-v2/__tests__/admission-gate.test.js +23 -6
- package/core/dist/runtime-v2/__tests__/admission-gate.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/context-payload-validation.test.js +1 -1
- package/core/dist/runtime-v2/__tests__/context-payload-validation.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js +20 -0
- package/core/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/evidence-guards.test.js +2 -2
- package/core/dist/runtime-v2/__tests__/evidence-guards.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/golden-path-diagnostician-e2e.test.js +1 -1
- package/core/dist/runtime-v2/__tests__/golden-path-diagnostician-e2e.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/pain-evidence-contract.test.js +3 -3
- package/core/dist/runtime-v2/__tests__/pain-evidence-contract.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.js +1 -1
- package/core/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js +26 -5
- package/core/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js.map +1 -1
- package/core/dist/runtime-v2/admission-gate.d.ts +8 -1
- package/core/dist/runtime-v2/admission-gate.d.ts.map +1 -1
- package/core/dist/runtime-v2/admission-gate.js +14 -0
- package/core/dist/runtime-v2/admission-gate.js.map +1 -1
- package/core/dist/runtime-v2/context-payload.d.ts +9 -3
- package/core/dist/runtime-v2/context-payload.d.ts.map +1 -1
- package/core/dist/runtime-v2/context-payload.js +3 -1
- package/core/dist/runtime-v2/context-payload.js.map +1 -1
- package/core/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js +2 -2
- package/core/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js.map +1 -1
- package/core/dist/runtime-v2/evidence-triage/observation-resolver.d.ts +1 -1
- package/core/dist/runtime-v2/evidence-triage/observation-resolver.d.ts.map +1 -1
- package/core/dist/runtime-v2/evidence-triage/observation-resolver.js +2 -2
- package/core/dist/runtime-v2/evidence-triage/observation-resolver.js.map +1 -1
- package/core/dist/runtime-v2/evidence-triage/types.d.ts +1 -1
- package/core/dist/runtime-v2/evidence-triage/types.d.ts.map +1 -1
- package/core/dist/runtime-v2/evolution/evolution-types.d.ts +8 -4
- package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/evolution/evolution-types.js +3 -1
- package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +9 -1
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +6 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-prompt-builder.test.js +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js +15 -14
- package/core/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js +56 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +883 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js +328 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +63 -2
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-output.js +139 -2
- package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +54 -2
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +49 -2
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +34 -0
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +295 -12
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js +10 -54
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.d.ts +7 -1
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.js +9 -2
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts +89 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.js +272 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.js.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts +12 -0
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-retry.js +23 -0
- package/core/dist/runtime-v2/internalization/owner-retry.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts +195 -0
- package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-review.js +470 -0
- package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -0
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +5 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +68 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.js +104 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/core/dist/runtime-v2/internalization/revision-reopen.d.ts +56 -0
- package/core/dist/runtime-v2/internalization/revision-reopen.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/revision-reopen.js +144 -0
- package/core/dist/runtime-v2/internalization/revision-reopen.js.map +1 -0
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +12 -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 +145 -14
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts +2 -0
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-bridge.js +4 -3
- package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
- package/core/dist/runtime-v2/pain-to-principle-service.d.ts +1 -0
- package/core/dist/runtime-v2/pain-to-principle-service.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-to-principle-service.js +1 -0
- package/core/dist/runtime-v2/pain-to-principle-service.js.map +1 -1
- package/core/dist/runtime-v2/runner/__tests__/pain-signal-bridge-admission.test.js +6 -6
- package/core/dist/runtime-v2/runner/__tests__/pain-signal-bridge-admission.test.js.map +1 -1
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js +25 -5
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js.map +1 -1
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.test.js +39 -9
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.test.js.map +1 -1
- 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 +9 -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.d.ts +2 -0
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js +78 -0
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js.map +1 -0
- package/core/dist/runtime-v2/store/task/memory-task-store.d.ts +1 -0
- package/core/dist/runtime-v2/store/task/memory-task-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/task/memory-task-store.js +8 -0
- package/core/dist/runtime-v2/store/task/memory-task-store.js.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +1 -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 +52 -0
- package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
- package/core/dist/runtime-v2/store/task/task-store.d.ts +9 -0
- package/core/dist/runtime-v2/store/task/task-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/workspace-isolation.test.js +1 -0
- package/core/dist/runtime-v2/store/workspace-isolation.test.js.map +1 -1
- package/core/dist/runtime-v2/task-status.d.ts +2 -1
- package/core/dist/runtime-v2/task-status.d.ts.map +1 -1
- package/core/dist/runtime-v2/task-status.js +5 -1
- package/core/dist/runtime-v2/task-status.js.map +1 -1
- package/host-runtime/dist/governance-observation-store.d.ts +7 -0
- package/host-runtime/dist/governance-observation-store.js +9 -0
- package/host-runtime/dist/governance-signal-admission.d.ts +228 -0
- package/host-runtime/dist/governance-signal-admission.js +841 -0
- package/host-runtime/dist/index.d.ts +1 -0
- package/host-runtime/dist/index.js +1 -0
- package/host-runtime/dist/production-pain-evidence.d.ts +60 -0
- package/host-runtime/dist/production-pain-evidence.js +73 -10
- package/package.json +2 -2
- package/pd-cli/dist/commands/codex-reconcile.d.ts +22 -0
- package/pd-cli/dist/commands/codex-reconcile.d.ts.map +1 -0
- package/pd-cli/dist/commands/codex-reconcile.js +61 -0
- package/pd-cli/dist/commands/codex-reconcile.js.map +1 -0
- package/pd-cli/dist/commands/pain-retry.d.ts.map +1 -1
- package/pd-cli/dist/commands/pain-retry.js +4 -2
- package/pd-cli/dist/commands/pain-retry.js.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-retry.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-retry.js +14 -0
- package/pd-cli/dist/commands/runtime-internalization-retry.js.map +1 -1
- package/pd-cli/dist/index.js +23 -0
- package/pd-cli/dist/index.js.map +1 -1
- package/plugin/dist/bundle.js +497 -486
- package/plugin/dist/core/signal-collector-host.d.ts +11 -0
- package/plugin/dist/core/signal-keyword-store.d.ts +12 -35
- package/plugin/dist/governance-audit.js +153 -153
- package/plugin/dist/hooks/triage-adapter.d.ts +1 -1
- package/plugin/dist/rulehost-evidence.js +180 -180
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +2 -2
|
@@ -18,6 +18,7 @@ import { handleActivationsRoute, disposeActivationsModels } from './routes/activ
|
|
|
18
18
|
import { handleReceiptsRoute, disposeReceiptsModels } from './routes/receipts.js';
|
|
19
19
|
import { handleApprovalsGroupedRoute, disposeApprovalsGroupedModels } from './routes/approvals-grouped.js';
|
|
20
20
|
import { handleGovernanceRoute, handleGovernanceExperienceRoute, resolveOwnerConfigSnapshot, disposeGovernanceModels } from './routes/governance.js';
|
|
21
|
+
import { handleOwnerDecisionsRoute } from './routes/owner-decisions.js';
|
|
21
22
|
import { handleOwnerIdentityRoute } from './routes/owner-identity.js';
|
|
22
23
|
import { handleEvidenceChainRoute, disposeEvidenceChainModels } from './routes/evidence-chain.js';
|
|
23
24
|
import { handleIntentRoute, disposeIntentModels } from './routes/intent.js';
|
|
@@ -418,6 +419,22 @@ function handleRequest(services) {
|
|
|
418
419
|
asyncHandler(() => handleGovernanceRoute(req, res, services.workspaceDir))(req, res);
|
|
419
420
|
return;
|
|
420
421
|
}
|
|
422
|
+
// PRI-629: GET/POST /api/v1/governance/owner-decisions — unified Owner Inbox
|
|
423
|
+
// (read projection + resolution). Identity derived server-side (SPEC §29):
|
|
424
|
+
// configured owner when registered, else the authenticated console operator.
|
|
425
|
+
if (urlPath === '/api/v1/governance/owner-decisions' || urlPath.startsWith('/api/v1/governance/owner-decisions/')) {
|
|
426
|
+
const subPath = urlPath.slice('/api/v1/governance/owner-decisions'.length);
|
|
427
|
+
const odIdentity = resolveOwnerIdentity(process.env, defaultOwnerIdentityHomeDir());
|
|
428
|
+
const authEnabledForOwner = services.authConfig.isEnabled() && odIdentity.ownerId && odIdentity.credentialId;
|
|
429
|
+
asyncHandler(() => handleOwnerDecisionsRoute(req, res, {
|
|
430
|
+
workspaceDir: services.workspaceDir,
|
|
431
|
+
subPath,
|
|
432
|
+
ownerIdentity: authEnabledForOwner
|
|
433
|
+
? { ownerId: odIdentity.ownerId ?? 'console_operator', credentialId: odIdentity.credentialId ?? undefined }
|
|
434
|
+
: { ownerId: 'console_operator' },
|
|
435
|
+
}))(req, res);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
421
438
|
// PRI-466/477: GET/POST/PUT /api/v1/intent (+ /init, /content sub-paths)
|
|
422
439
|
if (urlPath === '/api/v1/intent' || urlPath.startsWith('/api/v1/intent/')) {
|
|
423
440
|
const subPath = urlPath === '/api/v1/intent' ? '' : urlPath.slice('/api/v1/intent'.length);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type OwnerDecisionItemKind = 'evaluator_review' | 'rollout_review' | 'activation_approval' | 'rulecode_decision';
|
|
2
|
+
export interface OwnerDecisionItem {
|
|
3
|
+
readonly reviewKey: string;
|
|
4
|
+
readonly kind: OwnerDecisionItemKind;
|
|
5
|
+
readonly taskId: string;
|
|
6
|
+
readonly principleId?: string;
|
|
7
|
+
readonly title: string;
|
|
8
|
+
readonly summary: string;
|
|
9
|
+
readonly machineRecommendation?: string;
|
|
10
|
+
readonly score?: number;
|
|
11
|
+
readonly reasonCode: string;
|
|
12
|
+
readonly legacy: boolean;
|
|
13
|
+
readonly allowedActions: readonly string[];
|
|
14
|
+
/** stale 防护事实快照 — POST /resolve 原样回传,服务端重读比对 */
|
|
15
|
+
readonly expectedRevisionEpoch: number;
|
|
16
|
+
readonly expectedSourceRunId: string;
|
|
17
|
+
readonly expectedSourceArtifactId: string;
|
|
18
|
+
readonly expectedSourceArtifactHash: string;
|
|
19
|
+
readonly createdAt: string;
|
|
20
|
+
}
|
|
21
|
+
export interface OwnerDecisionListResult {
|
|
22
|
+
readonly items: readonly OwnerDecisionItem[];
|
|
23
|
+
readonly total: number;
|
|
24
|
+
readonly generatedAt: string;
|
|
25
|
+
}
|
|
26
|
+
export declare class OwnerDecisionConsoleModel {
|
|
27
|
+
private readonly workspaceDir;
|
|
28
|
+
constructor(workspaceDir: string);
|
|
29
|
+
/**
|
|
30
|
+
* 派生当前真实可执行的 Owner 决策列表(INV-01 / SPEC §27 badge 的唯一来源)。
|
|
31
|
+
* N = items.length — 不是 candidate 数、不是 NHR 总数、不是 failed 数。
|
|
32
|
+
*/
|
|
33
|
+
listOwnerDecisionItems(): Promise<OwnerDecisionListResult>;
|
|
34
|
+
/**
|
|
35
|
+
* failed-tasks 列表分流字段: 该 NHR 任务是否 decision-capable
|
|
36
|
+
* (decision-capable → 前往治理焦点;technical → Recover)。
|
|
37
|
+
*/
|
|
38
|
+
ownerDecisionRequiredTaskIds(): Promise<Set<string>>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OwnerDecisionConsoleModel — PRI-629 统一 Owner Decision 读模型(投影)。
|
|
3
|
+
*
|
|
4
|
+
* SPEC §21: 不新建 authority table — 本模型是纯派生 read model,从既有
|
|
5
|
+
* durable facts(tasks / pi_metadata / pi_artifacts / approvals / activations)
|
|
6
|
+
* 经 core 的 collectOwnerDecisionFacts + deriveOwnerDecisionCapability 派生
|
|
7
|
+
* OwnerDecisionItem。INV-01: 只有 allowedActions 非空的条目才进入列表。
|
|
8
|
+
*
|
|
9
|
+
* 策略全部在 @principles/core 的 owner-review.ts(单一深模块);
|
|
10
|
+
* 本模型只负责 workspace I/O 与条目包装(标题/摘要等展示字段)。
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import { SqliteConnection, SqlitePIArtifactStore, SqliteApprovalQueueStore, collectOwnerDecisionFacts, deriveOwnerDecisionCapability, } from '@principles/core/runtime-v2';
|
|
15
|
+
const GOVERNANCE_TASK_KINDS = new Set(['evaluator', 'rollout_reviewer']);
|
|
16
|
+
/** tasks.status 合法值(PDTaskStatus)运行时守卫。 */
|
|
17
|
+
const TASK_STATUSES = new Set(['pending', 'leased', 'succeeded', 'retry_wait', 'failed', 'needs_human_review']);
|
|
18
|
+
function isTaskStatus(v) {
|
|
19
|
+
return typeof v === 'string' && TASK_STATUSES.has(v);
|
|
20
|
+
}
|
|
21
|
+
/** 行 → TaskRecord 的窄映射(tasks 表全部列;snake_case 键名转换)。 */
|
|
22
|
+
function rowToTaskRecord(row) {
|
|
23
|
+
if (typeof row.task_id !== 'string' || typeof row.task_kind !== 'string' || typeof row.status !== 'string') {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
taskId: row.task_id,
|
|
28
|
+
taskKind: row.task_kind,
|
|
29
|
+
status: isTaskStatus(row.status) ? row.status : 'pending',
|
|
30
|
+
attemptCount: typeof row.attempt_count === 'number' ? row.attempt_count : 0,
|
|
31
|
+
maxAttempts: typeof row.max_attempts === 'number' ? row.max_attempts : 3,
|
|
32
|
+
createdAt: typeof row.created_at === 'string' ? row.created_at : '',
|
|
33
|
+
updatedAt: typeof row.updated_at === 'string' ? row.updated_at : '',
|
|
34
|
+
// lastError (PDErrorCategory 联合) 与 lease/input/result 列为事实收集器
|
|
35
|
+
// 不需要的字段 — 不映射 (避免对未校验枚举的 as 断言)。
|
|
36
|
+
...(typeof row.diagnostic_json === 'string' ? { diagnosticJson: row.diagnostic_json } : {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** 同一 readonly 连接上的 task 行读取闭包(依赖链遍历用)。 */
|
|
40
|
+
/** DB 行守卫: 非空普通对象(列读取处再逐字段 typeof 校验)。 */
|
|
41
|
+
function isRecordRow(v) {
|
|
42
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
43
|
+
}
|
|
44
|
+
function makeRowReader(db) {
|
|
45
|
+
const stmt = db.prepare('SELECT * FROM tasks WHERE task_id = ?');
|
|
46
|
+
return (taskId) => {
|
|
47
|
+
const row = stmt.get(taskId);
|
|
48
|
+
if (!isRecordRow(row))
|
|
49
|
+
return null;
|
|
50
|
+
return row ? rowToTaskRecord(row) : null;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** 行 → TaskRecord 的窄映射(tasks 表全部列;diagnostic_json 键名转换)。 */
|
|
54
|
+
/** 单任务 capability → 决策条目(不 eligible → null,INV-01)。 */
|
|
55
|
+
async function deriveTaskDecisionItem(task, artifactStore, readTaskRow) {
|
|
56
|
+
if (!GOVERNANCE_TASK_KINDS.has(task.taskKind))
|
|
57
|
+
return null;
|
|
58
|
+
const factStore = {
|
|
59
|
+
getTask: async (taskId) => {
|
|
60
|
+
if (taskId === task.taskId)
|
|
61
|
+
return task;
|
|
62
|
+
try {
|
|
63
|
+
return readTaskRow(taskId);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
listArtifactsBySourceTask: async (taskId) => {
|
|
70
|
+
const list = await artifactStore.listBySourceTaskId(taskId).catch(() => []);
|
|
71
|
+
return list.map((a) => ({
|
|
72
|
+
artifactId: a.artifactId,
|
|
73
|
+
artifactKind: a.artifactKind,
|
|
74
|
+
validationStatus: a.validationStatus,
|
|
75
|
+
contentJson: a.contentJson,
|
|
76
|
+
}));
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
const facts = await collectOwnerDecisionFacts(factStore, task.taskId);
|
|
80
|
+
if (!facts)
|
|
81
|
+
return null;
|
|
82
|
+
const capability = deriveOwnerDecisionCapability(facts);
|
|
83
|
+
if (!capability.eligible || !capability.reviewKey)
|
|
84
|
+
return null;
|
|
85
|
+
const sourceRunId = facts.task.humanReviewContext?.sourceRunId
|
|
86
|
+
?? facts.task.completionIntent?.sourceRunId ?? '';
|
|
87
|
+
const artifact = facts.decisionArtifact;
|
|
88
|
+
const kind = task.taskKind === 'rollout_reviewer' ? 'rollout_review' : 'evaluator_review';
|
|
89
|
+
return {
|
|
90
|
+
reviewKey: capability.reviewKey,
|
|
91
|
+
kind,
|
|
92
|
+
taskId: task.taskId,
|
|
93
|
+
title: kind === 'rollout_review'
|
|
94
|
+
? 'Rollout 评审无法收敛'
|
|
95
|
+
: '自动改进已达到本轮上限',
|
|
96
|
+
summary: `机器建议继续修改(${capability.reasonCode}${capability.legacy ? ',由历史数据安全推断' : ''})。需要你的判断:接受当前版本、再修改一次,或拒绝。`,
|
|
97
|
+
machineRecommendation: 'needs_revision(继续修改)',
|
|
98
|
+
reasonCode: capability.reasonCode,
|
|
99
|
+
legacy: capability.legacy,
|
|
100
|
+
allowedActions: [...capability.allowedActions],
|
|
101
|
+
expectedRevisionEpoch: facts.task.revisionCount ?? 0,
|
|
102
|
+
expectedSourceRunId: sourceRunId,
|
|
103
|
+
expectedSourceArtifactId: artifact?.artifactId ?? '',
|
|
104
|
+
expectedSourceArtifactHash: artifact?.contentHash ?? '',
|
|
105
|
+
createdAt: task.updatedAt,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export class OwnerDecisionConsoleModel {
|
|
109
|
+
workspaceDir;
|
|
110
|
+
constructor(workspaceDir) {
|
|
111
|
+
this.workspaceDir = workspaceDir;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 派生当前真实可执行的 Owner 决策列表(INV-01 / SPEC §27 badge 的唯一来源)。
|
|
115
|
+
* N = items.length — 不是 candidate 数、不是 NHR 总数、不是 failed 数。
|
|
116
|
+
*/
|
|
117
|
+
async listOwnerDecisionItems() {
|
|
118
|
+
const stateDbPath = path.join(this.workspaceDir, '.pd', 'state.db');
|
|
119
|
+
if (!fs.existsSync(stateDbPath)) {
|
|
120
|
+
return { items: [], total: 0, generatedAt: new Date().toISOString() };
|
|
121
|
+
}
|
|
122
|
+
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: true });
|
|
123
|
+
try {
|
|
124
|
+
const db = conn.getDb();
|
|
125
|
+
const artifactStore = new SqlitePIArtifactStore(conn);
|
|
126
|
+
const items = [];
|
|
127
|
+
// ── 1. needs_human_review 的 decision-capable 任务 (PRI-629 核心) ──
|
|
128
|
+
let nhrRows = [];
|
|
129
|
+
try {
|
|
130
|
+
nhrRows = db.prepare(`SELECT * FROM tasks WHERE status = 'needs_human_review' AND task_kind IN ('evaluator', 'rollout_reviewer')`).all().filter(isRecordRow);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
nhrRows = []; // tasks 表缺失(旧库)→ 无决策项
|
|
134
|
+
}
|
|
135
|
+
for (const row of nhrRows) {
|
|
136
|
+
const task = rowToTaskRecord(row);
|
|
137
|
+
if (!task)
|
|
138
|
+
continue;
|
|
139
|
+
const item = await deriveTaskDecisionItem(task, artifactStore, makeRowReader(db));
|
|
140
|
+
if (item)
|
|
141
|
+
items.push(item);
|
|
142
|
+
}
|
|
143
|
+
// ── 2. 既有 approvals pending(高风险部署审批 — 独立门,动作走既有 API)──
|
|
144
|
+
try {
|
|
145
|
+
const approvals = await new SqliteApprovalQueueStore(conn).listPending();
|
|
146
|
+
for (const approval of approvals) {
|
|
147
|
+
items.push({
|
|
148
|
+
reviewKey: `apr:${approval.approvalId}`,
|
|
149
|
+
kind: 'activation_approval',
|
|
150
|
+
taskId: approval.artifactId,
|
|
151
|
+
title: `部署审批 · ${approval.channel}`,
|
|
152
|
+
summary: `${approval.riskLevel} 风险通道 ${approval.channel} 的原则部署等待批准。`,
|
|
153
|
+
reasonCode: 'activation_approval_pending',
|
|
154
|
+
legacy: false,
|
|
155
|
+
allowedActions: ['approve', 'reject'],
|
|
156
|
+
expectedRevisionEpoch: 0,
|
|
157
|
+
expectedSourceRunId: '',
|
|
158
|
+
expectedSourceArtifactId: approval.artifactId,
|
|
159
|
+
expectedSourceArtifactHash: '',
|
|
160
|
+
createdAt: approval.requestedAt,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// approvals 表缺失 → 跳过(恒空也是常态:低风险自动激活)
|
|
166
|
+
}
|
|
167
|
+
// ── 3. RuleCode shadow 待决(promote / reject — 独立 authority,动作走既有 API)──
|
|
168
|
+
try {
|
|
169
|
+
const shadowRows = db.prepare(`SELECT activation_id, channel, activated_at FROM activations
|
|
170
|
+
WHERE action = 'code_tool_hook_shadow_activate'
|
|
171
|
+
AND promoted_at IS NULL AND deactivated_at IS NULL`).all().filter(isRecordRow);
|
|
172
|
+
for (const row of shadowRows) {
|
|
173
|
+
items.push({
|
|
174
|
+
reviewKey: `rulecode:${String(row.activation_id ?? '')}`,
|
|
175
|
+
kind: 'rulecode_decision',
|
|
176
|
+
taskId: String(row.activation_id ?? ''),
|
|
177
|
+
title: 'RuleCode 影子规则待决',
|
|
178
|
+
summary: '一条 code_tool_hook 规则处于影子激活期,等待拥有者裁决(提升 / 拒绝)。',
|
|
179
|
+
reasonCode: 'rulecode_shadow_pending_promotion',
|
|
180
|
+
legacy: false,
|
|
181
|
+
allowedActions: ['promote', 'reject_after_shadow'],
|
|
182
|
+
expectedRevisionEpoch: 0,
|
|
183
|
+
expectedSourceRunId: '',
|
|
184
|
+
expectedSourceArtifactId: String(row.activation_id ?? ''),
|
|
185
|
+
expectedSourceArtifactHash: '',
|
|
186
|
+
createdAt: typeof row.activated_at === 'string' ? row.activated_at : '',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// activations 表缺失 → 跳过
|
|
192
|
+
}
|
|
193
|
+
return { items, total: items.length, generatedAt: new Date().toISOString() };
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
conn.close();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* failed-tasks 列表分流字段: 该 NHR 任务是否 decision-capable
|
|
201
|
+
* (decision-capable → 前往治理焦点;technical → Recover)。
|
|
202
|
+
*/
|
|
203
|
+
async ownerDecisionRequiredTaskIds() {
|
|
204
|
+
const stateDbPath = path.join(this.workspaceDir, '.pd', 'state.db');
|
|
205
|
+
if (!fs.existsSync(stateDbPath))
|
|
206
|
+
return new Set();
|
|
207
|
+
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: true });
|
|
208
|
+
try {
|
|
209
|
+
const db = conn.getDb();
|
|
210
|
+
const artifactStore = new SqlitePIArtifactStore(conn);
|
|
211
|
+
const ids = new Set();
|
|
212
|
+
let rows = [];
|
|
213
|
+
try {
|
|
214
|
+
rows = db.prepare(`SELECT * FROM tasks WHERE status = 'needs_human_review' AND task_kind IN ('evaluator', 'rollout_reviewer')`).all().filter(isRecordRow);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return ids;
|
|
218
|
+
}
|
|
219
|
+
for (const row of rows) {
|
|
220
|
+
const task = rowToTaskRecord(row);
|
|
221
|
+
if (!task)
|
|
222
|
+
continue;
|
|
223
|
+
const item = await deriveTaskDecisionItem(task, artifactStore, makeRowReader(db));
|
|
224
|
+
if (item)
|
|
225
|
+
ids.add(task.taskId);
|
|
226
|
+
}
|
|
227
|
+
return ids;
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
conn.close();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Classification is purely read-side; it does NOT mutate any data.
|
|
14
14
|
*/
|
|
15
15
|
import type { PrincipleListItem } from './PrinciplesConsoleModel.js';
|
|
16
|
-
export type PrincipleCategory = 'owner_actionable' | 'demo' | 'smoke' | 'historical' | 'builtin' | 'already_decided';
|
|
16
|
+
export type PrincipleCategory = 'owner_actionable' | 'in_pipeline' | 'demo' | 'smoke' | 'historical' | 'builtin' | 'already_decided';
|
|
17
17
|
export interface ClassifiedPrinciple {
|
|
18
18
|
principle: PrincipleListItem;
|
|
19
19
|
category: PrincipleCategory;
|
|
@@ -26,7 +26,7 @@ export interface ClassifiedPrinciple {
|
|
|
26
26
|
* via the approval queue (approved or rejected). These should be classified
|
|
27
27
|
* as already_decided regardless of their ledger status.
|
|
28
28
|
*/
|
|
29
|
-
export declare function classifyPrinciple(p: PrincipleListItem, decidedPrincipleIds?: Set<string>): PrincipleCategory;
|
|
29
|
+
export declare function classifyPrinciple(p: PrincipleListItem, decidedPrincipleIds?: Set<string>, pendingApprovalPrincipleIds?: Set<string>): PrincipleCategory;
|
|
30
30
|
/**
|
|
31
31
|
* Classify a batch of principles. Returns the classified array.
|
|
32
32
|
*
|
|
@@ -34,7 +34,7 @@ export declare function classifyPrinciple(p: PrincipleListItem, decidedPrinciple
|
|
|
34
34
|
* @param decidedPrincipleIds - Set of principle IDs that have been decided
|
|
35
35
|
* via the approval queue (approved or rejected).
|
|
36
36
|
*/
|
|
37
|
-
export declare function classifyPrinciples(principles: PrincipleListItem[], decidedPrincipleIds?: Set<string>): ClassifiedPrinciple[];
|
|
37
|
+
export declare function classifyPrinciples(principles: PrincipleListItem[], decidedPrincipleIds?: Set<string>, pendingApprovalPrincipleIds?: Set<string>): ClassifiedPrinciple[];
|
|
38
38
|
/**
|
|
39
39
|
* Filter to only owner-actionable principles.
|
|
40
40
|
*/
|
|
@@ -47,7 +47,7 @@ function isBuiltinId(id) {
|
|
|
47
47
|
* via the approval queue (approved or rejected). These should be classified
|
|
48
48
|
* as already_decided regardless of their ledger status.
|
|
49
49
|
*/
|
|
50
|
-
export function classifyPrinciple(p, decidedPrincipleIds) {
|
|
50
|
+
export function classifyPrinciple(p, decidedPrincipleIds, pendingApprovalPrincipleIds) {
|
|
51
51
|
// 1. Builtin axioms (T-01..T-10) are never governance targets
|
|
52
52
|
if (isBuiltinId(p.id)) {
|
|
53
53
|
return 'builtin';
|
|
@@ -74,6 +74,11 @@ export function classifyPrinciple(p, decidedPrincipleIds) {
|
|
|
74
74
|
if (decidedPrincipleIds && decidedPrincipleIds.has(p.id)) {
|
|
75
75
|
return 'already_decided';
|
|
76
76
|
}
|
|
77
|
+
// 4b. PRI-629: a PENDING approval is a real Owner decision — owner_actionable
|
|
78
|
+
// (INV-01: actionability requires an executable action, and it exists here).
|
|
79
|
+
if (pendingApprovalPrincipleIds && pendingApprovalPrincipleIds.has(p.id)) {
|
|
80
|
+
return 'owner_actionable';
|
|
81
|
+
}
|
|
77
82
|
// 5. Already decided: active = approved & in effect, no governance needed
|
|
78
83
|
if (p.status === 'active') {
|
|
79
84
|
return 'already_decided';
|
|
@@ -82,8 +87,10 @@ export function classifyPrinciple(p, decidedPrincipleIds) {
|
|
|
82
87
|
if (p.status === 'archived' || p.status === 'deprecated') {
|
|
83
88
|
return 'historical';
|
|
84
89
|
}
|
|
85
|
-
// 7.
|
|
86
|
-
|
|
90
|
+
// 7. candidate / probation = 内化管线进行中 (PRI-629 INV-02: lifecycle ≠
|
|
91
|
+
// Owner attention)。真正的 Owner 决策由治理焦点的 Owner Decision
|
|
92
|
+
// 投影呈现;此处不再产生假"待审查"。
|
|
93
|
+
return 'in_pipeline';
|
|
87
94
|
}
|
|
88
95
|
/**
|
|
89
96
|
* Classify a batch of principles. Returns the classified array.
|
|
@@ -92,8 +99,8 @@ export function classifyPrinciple(p, decidedPrincipleIds) {
|
|
|
92
99
|
* @param decidedPrincipleIds - Set of principle IDs that have been decided
|
|
93
100
|
* via the approval queue (approved or rejected).
|
|
94
101
|
*/
|
|
95
|
-
export function classifyPrinciples(principles, decidedPrincipleIds) {
|
|
96
|
-
return principles.map((p) => ({ principle: p, category: classifyPrinciple(p, decidedPrincipleIds) }));
|
|
102
|
+
export function classifyPrinciples(principles, decidedPrincipleIds, pendingApprovalPrincipleIds) {
|
|
103
|
+
return principles.map((p) => ({ principle: p, category: classifyPrinciple(p, decidedPrincipleIds, pendingApprovalPrincipleIds) }));
|
|
97
104
|
}
|
|
98
105
|
/**
|
|
99
106
|
* Filter to only owner-actionable principles.
|
|
@@ -76,7 +76,7 @@ export declare class PrinciplesConsoleModel {
|
|
|
76
76
|
constructor(workspaceDir: string);
|
|
77
77
|
private getLedgerPath;
|
|
78
78
|
private loadLedger;
|
|
79
|
-
listPrinciples(filter?: PrincipleFilter, decidedPrincipleIds?: Set<string>): Promise<PrinciplesListOutput>;
|
|
79
|
+
listPrinciples(filter?: PrincipleFilter, decidedPrincipleIds?: Set<string>, pendingApprovalPrincipleIds?: Set<string>): Promise<PrinciplesListOutput>;
|
|
80
80
|
getPrincipleDetail(principleId: string): Promise<PrincipleDetailOutput | null>;
|
|
81
81
|
archivePrinciple(principleId: string): Promise<boolean>;
|
|
82
82
|
unarchivePrinciple(principleId: string): Promise<boolean>;
|
|
@@ -125,7 +125,7 @@ export class PrinciplesConsoleModel {
|
|
|
125
125
|
this.cacheTimestamp = now;
|
|
126
126
|
return ledger;
|
|
127
127
|
}
|
|
128
|
-
async listPrinciples(filter, decidedPrincipleIds) {
|
|
128
|
+
async listPrinciples(filter, decidedPrincipleIds, pendingApprovalPrincipleIds) {
|
|
129
129
|
const ledger = this.loadLedger();
|
|
130
130
|
const principles = Object.values(ledger.tree.principles);
|
|
131
131
|
const summary = {
|
|
@@ -181,7 +181,7 @@ export class PrinciplesConsoleModel {
|
|
|
181
181
|
}
|
|
182
182
|
items.sort((a, b) => b.valueScore - a.valueScore);
|
|
183
183
|
// PRI-330: classify and optionally filter
|
|
184
|
-
const classified = classifyPrinciples(items, decidedPrincipleIds);
|
|
184
|
+
const classified = classifyPrinciples(items, decidedPrincipleIds, pendingApprovalPrincipleIds);
|
|
185
185
|
const categories = {};
|
|
186
186
|
for (const c of classified) {
|
|
187
187
|
categories[c.category] = (categories[c.category] ?? 0) + 1;
|
|
@@ -25,6 +25,7 @@ import * as path from 'node:path';
|
|
|
25
25
|
import { SqliteConnection, SqliteTaskStore, PDRuntimeError, createRecoverySweepService, appendRecoveryAction, } from '@principles/core/runtime-v2';
|
|
26
26
|
import { sendSuccess, sendError, sendNotFound, sendMethodNotAllowed, sendBadRequest, } from '../utils/response.js';
|
|
27
27
|
import { readBody } from '../utils/request.js';
|
|
28
|
+
import { OwnerDecisionConsoleModel } from '../models/OwnerDecisionConsoleModel.js';
|
|
28
29
|
const entries = new Map();
|
|
29
30
|
function stateDbExists(workspaceDir) {
|
|
30
31
|
return fs.existsSync(path.join(workspaceDir, '.pd', 'state.db'));
|
|
@@ -149,6 +150,15 @@ function parseRecoverBody(body) {
|
|
|
149
150
|
* is raised (core raises it by +3). needs_human_review resets ignore it —
|
|
150
151
|
* the owner authority reset has its own budget semantics.
|
|
151
152
|
*/
|
|
153
|
+
const ownerDecisionModels = new Map();
|
|
154
|
+
function getOwnerDecisionModel(workspaceDir) {
|
|
155
|
+
let model = ownerDecisionModels.get(workspaceDir);
|
|
156
|
+
if (!model) {
|
|
157
|
+
model = new OwnerDecisionConsoleModel(workspaceDir);
|
|
158
|
+
ownerDecisionModels.set(workspaceDir, model);
|
|
159
|
+
}
|
|
160
|
+
return model;
|
|
161
|
+
}
|
|
152
162
|
async function dispatchRecovery(workspaceDir, taskId, force) {
|
|
153
163
|
const { service, close } = await createRecoverySweepService({ workspaceDir });
|
|
154
164
|
try {
|
|
@@ -184,6 +194,19 @@ async function dispatchRecovery(workspaceDir, taskId, force) {
|
|
|
184
194
|
}
|
|
185
195
|
// not failed (or missing) → owner authority reset path
|
|
186
196
|
const outcome = await service.recoverNeedsHumanReviewTask(taskId);
|
|
197
|
+
if (outcome.status === 'rejected') {
|
|
198
|
+
// PRI-629 Recover guard (SPEC §17): decision-capable 人工裁决不允许
|
|
199
|
+
// authority reset — Recover 不是治理出口,出口在治理焦点 Owner Decision。
|
|
200
|
+
return {
|
|
201
|
+
ok: false,
|
|
202
|
+
failure: {
|
|
203
|
+
httpStatus: 409,
|
|
204
|
+
errorCode: 'owner_decision_required',
|
|
205
|
+
message: 'This task awaits an Owner decision (accept current / revise once / reject). Recover is not a governance exit and was refused.',
|
|
206
|
+
nextAction: 'Open the Console governance focus (#/focus) and resolve the decision there.',
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
187
210
|
if (outcome.status === 'requeued') {
|
|
188
211
|
return {
|
|
189
212
|
ok: true,
|
|
@@ -307,19 +330,34 @@ export async function handleFailedTasksRoute(req, res, ctx) {
|
|
|
307
330
|
const store = ctx.sqliteTaskStore ?? getStore(workspaceDir);
|
|
308
331
|
const tasks = await store.listFailedTasks({ kind, since, limit: limitResult, offset: offsetResult });
|
|
309
332
|
const total = await store.countFailedTasks({ kind, since });
|
|
333
|
+
// PRI-629 分流: decision-capable NHR 任务标 ownerDecisionRequired —
|
|
334
|
+
// UI 隐藏 Recover、引导前往治理焦点 (决策出口不在失败页)。
|
|
335
|
+
let decisionRequiredIds = null;
|
|
336
|
+
try {
|
|
337
|
+
decisionRequiredIds = await getOwnerDecisionModel(workspaceDir).ownerDecisionRequiredTaskIds();
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
decisionRequiredIds = null; // 投影失败不阻塞失败列表 (rc-9 降级由 UI 兜底)
|
|
341
|
+
}
|
|
342
|
+
const enriched = tasks.map((task) => ({
|
|
343
|
+
...task,
|
|
344
|
+
...(decisionRequiredIds !== null && task.status === 'needs_human_review'
|
|
345
|
+
? { ownerDecisionRequired: decisionRequiredIds.has(task.taskId ?? '') }
|
|
346
|
+
: {}),
|
|
347
|
+
}));
|
|
310
348
|
// Only report "PD pipeline is healthy" when total === 0. When
|
|
311
349
|
// tasks.length === 0 but total > 0, the caller has paginated past the
|
|
312
350
|
// last page — returning "healthy" here would mask the failures that
|
|
313
351
|
// exist on earlier pages.
|
|
314
352
|
if (total === 0) {
|
|
315
353
|
sendSuccess(res, {
|
|
316
|
-
tasks,
|
|
354
|
+
tasks: enriched,
|
|
317
355
|
total,
|
|
318
356
|
nextAction: 'No failed tasks. PD pipeline is healthy.',
|
|
319
357
|
});
|
|
320
358
|
}
|
|
321
359
|
else {
|
|
322
|
-
sendSuccess(res, { tasks, total });
|
|
360
|
+
sendSuccess(res, { tasks: enriched, total });
|
|
323
361
|
}
|
|
324
362
|
}
|
|
325
363
|
catch (err) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRI-629 — /api/v1/governance/owner-decisions 路由。
|
|
3
|
+
*
|
|
4
|
+
* GET /api/v1/governance/owner-decisions — 统一 Owner Inbox 投影
|
|
5
|
+
* POST /api/v1/governance/owner-decisions/:taskId/resolve — Owner 裁决
|
|
6
|
+
*
|
|
7
|
+
* 安全模型(SPEC §28/§29):
|
|
8
|
+
* - 全局 console token 认证由 server/index.ts 统一强制;
|
|
9
|
+
* - 身份从 server-side auth context 推导(configured_owner 优先,其次
|
|
10
|
+
* console operator),body 里的身份字段一律不信任;
|
|
11
|
+
* - effectiveDecision / eligibility 全部服务端重读 durable facts 判定;
|
|
12
|
+
* - stale 防护: reviewKey + expected* 事实快照逐字段比对(409)。
|
|
13
|
+
*/
|
|
14
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
15
|
+
export interface OwnerDecisionRouteContext {
|
|
16
|
+
workspaceDir: string;
|
|
17
|
+
/** server-side auth context 推导的身份(SPEC §29 — 不信任 body) */
|
|
18
|
+
ownerIdentity: {
|
|
19
|
+
ownerId: string;
|
|
20
|
+
credentialId?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare function handleOwnerDecisionsRoute(req: IncomingMessage, res: ServerResponse, ctx: OwnerDecisionRouteContext & {
|
|
24
|
+
subPath: string;
|
|
25
|
+
}): Promise<void>;
|