create-principles-disciple 1.145.1 → 1.145.4
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/_release/product-identity.json +2 -2
- package/codex-adapter/dist/worker/workspace-worker.js +2 -1
- package/codex-adapter/package.json +1 -1
- package/console/dist/server/config/pd-config-store.js +3 -0
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +4 -4
- package/console/dist/ui/i18n/en.json +17 -16
- package/console/dist/ui/i18n/zh-CN.json +17 -16
- package/console/dist/ui/pages/focus/FocusPage.js +9 -5
- package/console/dist/ui/pages/focus/OwnerDecisionCard.js +1 -1
- package/console/dist/ui/pages/principles/PrincipleDetailPage.js +10 -4
- package/console/dist/ui/pages/principles/PrinciplesPage.js +1 -1
- package/console/dist/ui/utils/reading-layout.d.ts +19 -0
- package/console/dist/ui/utils/reading-layout.js +46 -0
- package/console/dist/web/assets/app.js +30 -28
- package/core/dist/runtime-v2/index.d.ts +13 -3
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +6 -1
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +25 -6
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +21 -7
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-run-correlation.d.ts +82 -0
- package/core/dist/runtime-v2/internalization/artificer-run-correlation.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/artificer-run-correlation.js +79 -0
- package/core/dist/runtime-v2/internalization/artificer-run-correlation.js.map +1 -0
- package/core/dist/runtime-v2/internalization/artificer-runner.d.ts +16 -2
- package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-runner.js +89 -9
- package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.d.ts +141 -0
- package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.js +284 -0
- package/core/dist/runtime-v2/internalization/consumer-evidence-preflight.js.map +1 -0
- package/core/dist/runtime-v2/internalization/evidence-readiness.d.ts +170 -0
- package/core/dist/runtime-v2/internalization/evidence-readiness.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/evidence-readiness.js +335 -0
- package/core/dist/runtime-v2/internalization/evidence-readiness.js.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-evidence-recovery.d.ts +190 -0
- package/core/dist/runtime-v2/internalization/owner-evidence-recovery.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-evidence-recovery.js +488 -0
- package/core/dist/runtime-v2/internalization/owner-evidence-recovery.js.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts +23 -1
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-retry.js +19 -3
- package/core/dist/runtime-v2/internalization/owner-retry.js.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +16 -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 +44 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.js +75 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/core/dist/runtime-v2/recovery-actions-log.d.ts +55 -0
- package/core/dist/runtime-v2/recovery-actions-log.d.ts.map +1 -1
- package/core/dist/runtime-v2/recovery-actions-log.js +84 -3
- package/core/dist/runtime-v2/recovery-actions-log.js.map +1 -1
- package/core/dist/runtime-v2/recovery-sweep-service.d.ts +116 -1
- package/core/dist/runtime-v2/recovery-sweep-service.d.ts.map +1 -1
- package/core/dist/runtime-v2/recovery-sweep-service.js +252 -13
- package/core/dist/runtime-v2/recovery-sweep-service.js.map +1 -1
- package/core/dist/runtime-v2/runtime-state-handle.d.ts +8 -0
- package/core/dist/runtime-v2/runtime-state-handle.d.ts.map +1 -1
- package/core/dist/runtime-v2/runtime-state-handle.js +1 -0
- package/core/dist/runtime-v2/runtime-state-handle.js.map +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +16 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.js +7 -2
- package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
- package/core/dist/runtime-v2/store/task/memory-task-store.d.ts +7 -1
- 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 +16 -1
- 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 +7 -1
- 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 +31 -2
- 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 +19 -5
- package/core/dist/runtime-v2/store/task/task-store.d.ts.map +1 -1
- package/core/dist/telemetry-event.d.ts +2 -2
- package/core/dist/telemetry-event.d.ts.map +1 -1
- package/core/dist/telemetry-event.js +13 -0
- package/core/dist/telemetry-event.js.map +1 -1
- package/core/package.json +5 -1
- package/dist/installer.d.ts +1 -1
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +5 -1
- package/dist/installer.js.map +1 -1
- package/dist/installers/workbuddy-host-installer.d.ts +16 -3
- package/dist/installers/workbuddy-host-installer.d.ts.map +1 -1
- package/dist/installers/workbuddy-host-installer.js +62 -11
- package/dist/installers/workbuddy-host-installer.js.map +1 -1
- package/dist/manifest-host-union.d.ts +23 -0
- package/dist/manifest-host-union.d.ts.map +1 -0
- package/dist/manifest-host-union.js +22 -0
- package/dist/manifest-host-union.js.map +1 -0
- package/dist/uninstaller.d.ts +2 -1
- package/dist/uninstaller.d.ts.map +1 -1
- package/dist/uninstaller.js +7 -1
- package/dist/uninstaller.js.map +1 -1
- package/host-runtime/dist/behavior-example-pack-assembler.d.ts +41 -0
- package/host-runtime/dist/behavior-example-pack-assembler.js +243 -0
- package/host-runtime/dist/behavior-history.d.ts +6 -0
- package/host-runtime/dist/behavior-history.js +76 -0
- package/host-runtime/dist/index.d.ts +3 -0
- package/host-runtime/dist/index.js +3 -0
- package/host-runtime/dist/internalization-consumer-cycle.d.ts +12 -1
- package/host-runtime/dist/internalization-consumer-cycle.js +141 -16
- package/host-runtime/dist/workspace-behavior-evidence.d.ts +3 -0
- package/host-runtime/dist/workspace-behavior-evidence.js +74 -0
- package/host-runtime/dist/workspace-telemetry-emitter.js +13 -0
- package/host-runtime/package.json +10 -2
- package/install-layout/dist/index.d.ts +40 -1
- package/install-layout/dist/index.js +88 -6
- package/install-layout/package.json +1 -1
- package/package.json +3 -3
- package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.d.ts +44 -0
- package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.d.ts.map +1 -0
- package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.js +138 -0
- package/pd-cli/dist/commands/runtime-internalization-audit-artificer-evidence.js.map +1 -0
- package/pd-cli/dist/commands/runtime-internalization-evidence.d.ts +71 -0
- package/pd-cli/dist/commands/runtime-internalization-evidence.d.ts.map +1 -0
- package/pd-cli/dist/commands/runtime-internalization-evidence.js +405 -0
- package/pd-cli/dist/commands/runtime-internalization-evidence.js.map +1 -0
- package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.js +22 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js +45 -4
- package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
- package/pd-cli/dist/commands/runtime-recovery-failed-task.d.ts +22 -0
- package/pd-cli/dist/commands/runtime-recovery-failed-task.d.ts.map +1 -0
- package/pd-cli/dist/commands/runtime-recovery-failed-task.js +470 -0
- package/pd-cli/dist/commands/runtime-recovery-failed-task.js.map +1 -0
- package/pd-cli/dist/commands/runtime-recovery-failed-tasks.d.ts +24 -0
- package/pd-cli/dist/commands/runtime-recovery-failed-tasks.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-recovery-failed-tasks.js +4 -1
- package/pd-cli/dist/commands/runtime-recovery-failed-tasks.js.map +1 -1
- package/pd-cli/dist/index.js +13 -0
- package/pd-cli/dist/index.js.map +1 -1
- package/pd-cli/dist/services/artificer-evidence-audit.d.ts +156 -0
- package/pd-cli/dist/services/artificer-evidence-audit.d.ts.map +1 -0
- package/pd-cli/dist/services/artificer-evidence-audit.js +321 -0
- package/pd-cli/dist/services/artificer-evidence-audit.js.map +1 -0
- package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/pd-cli/dist/services/rulehost-pipeline-runner.js +17 -3
- package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +545 -541
- package/plugin/dist/core/behavior-example-pack-assembler.d.ts +4 -32
- package/plugin/dist/core/rule-context-assembler.d.ts +0 -9
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/dist/rulehost-evidence.js +27 -27
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/dist/installer.d.ts +1 -1
- package/release-manager/dist/installer.d.ts.map +1 -1
- package/release-manager/dist/installer.js +5 -1
- package/release-manager/dist/installer.js.map +1 -1
- package/release-manager/dist/installers/workbuddy-host-installer.d.ts +16 -3
- package/release-manager/dist/installers/workbuddy-host-installer.d.ts.map +1 -1
- package/release-manager/dist/installers/workbuddy-host-installer.js +62 -11
- package/release-manager/dist/installers/workbuddy-host-installer.js.map +1 -1
- package/release-manager/dist/manifest-host-union.d.ts +23 -0
- package/release-manager/dist/manifest-host-union.d.ts.map +1 -0
- package/release-manager/dist/manifest-host-union.js +22 -0
- package/release-manager/dist/manifest-host-union.js.map +1 -0
- package/release-manager/dist/uninstaller.d.ts +2 -1
- package/release-manager/dist/uninstaller.d.ts.map +1 -1
- package/release-manager/dist/uninstaller.js +7 -1
- package/release-manager/dist/uninstaller.js.map +1 -1
- package/release-manager/package.json +2 -2
- package/workbuddy-adapter/package.json +1 -1
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
import fs from 'node:fs';
|
|
29
29
|
import path from 'node:path';
|
|
30
30
|
import { computeFeatureFlagsFromConfig, createRuntimeStateHandle, createPainSignalBridge, isRetryWaitBackoffElapsed, PrincipleTreeLedgerAdapter, resolveDiagnosticianCapability, } from '@principles/core/runtime-v2';
|
|
31
|
-
import { loadPdConfigForPlugin, loadFeatureFlagFromConfig, reconcileGovernanceContinuation, runInternalizationConsumerCycle, saveHostToolDeclaration, } from '@principles/host-runtime';
|
|
31
|
+
import { createWorkspaceBehaviorEvidencePort, loadPdConfigForPlugin, loadFeatureFlagFromConfig, reconcileGovernanceContinuation, runInternalizationConsumerCycle, saveHostToolDeclaration, } from '@principles/host-runtime';
|
|
32
32
|
import { catchUpCodexIngestion } from '../ingestion/catch-up.js';
|
|
33
33
|
import { CODEX_TOOL_SEMANTIC_MAPPINGS, CODEX_TOOL_SEMANTICS } from '../tool-semantics.js';
|
|
34
34
|
const WORKER_OWNER = 'companion-worker';
|
|
@@ -406,6 +406,7 @@ export async function runCodexWorkspaceWorkerCycle(options) {
|
|
|
406
406
|
logLabel: 'CodexWorker',
|
|
407
407
|
logger,
|
|
408
408
|
emitEvent,
|
|
409
|
+
behaviorEvidence: createWorkspaceBehaviorEvidencePort(workspaceDir, CODEX_TOOL_SEMANTICS),
|
|
409
410
|
toolSemantics: CODEX_TOOL_SEMANTICS,
|
|
410
411
|
// PRI-741: artificer generation gets the Codex host projection
|
|
411
412
|
// (Bash/apply_patch — evidence-bound, intentionally sparse).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principles/codex-adapter",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Codex CLI host adapter for Principles Disciple — implements HostAdapter interface for OpenAI Codex CLI's stdin/stdout JSON hook model (ADR-0020).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1036,6 +1036,9 @@ function extractProfileFields(payload, type) {
|
|
|
1036
1036
|
if (Object.hasOwn(payload, 'maxRetries') && typeof payload.maxRetries === 'number') {
|
|
1037
1037
|
result.maxRetries = payload.maxRetries;
|
|
1038
1038
|
}
|
|
1039
|
+
if (Object.hasOwn(payload, 'maxTokens')) {
|
|
1040
|
+
result.maxTokens = payload.maxTokens;
|
|
1041
|
+
}
|
|
1039
1042
|
if (Object.hasOwn(payload, 'reasoning') && isReasoningLevel(payload.reasoning)) {
|
|
1040
1043
|
result.reasoning = payload.reasoning;
|
|
1041
1044
|
}
|
|
@@ -64,7 +64,7 @@ function extractCandidateDescription(contentJson) {
|
|
|
64
64
|
const principleText = principleRaw ? principleRaw.trim() : null;
|
|
65
65
|
const ruleText = ruleRaw ? ruleRaw.trim() : null;
|
|
66
66
|
if (principleText && ruleText)
|
|
67
|
-
return `${principleText}
|
|
67
|
+
return `${principleText} — ${ruleText}`;
|
|
68
68
|
if (ruleText)
|
|
69
69
|
return ruleText;
|
|
70
70
|
if (principleText)
|
|
@@ -79,7 +79,7 @@ function extractCandidateDescription(contentJson) {
|
|
|
79
79
|
const title = typeof draft.title === 'string' ? draft.title.trim() : '';
|
|
80
80
|
const statement = typeof draft.statement === 'string' ? draft.statement.trim() : '';
|
|
81
81
|
if (title && statement)
|
|
82
|
-
return `${title}
|
|
82
|
+
return `${title} — ${statement}`;
|
|
83
83
|
if (title)
|
|
84
84
|
return title;
|
|
85
85
|
if (statement)
|
|
@@ -148,7 +148,7 @@ export class ApprovalsGroupedConsoleModel {
|
|
|
148
148
|
async getApprovalsGrouped(targetHost) {
|
|
149
149
|
const stateDbPath = path.join(this.workspaceDir, '.pd', 'state.db');
|
|
150
150
|
if (!fs.existsSync(stateDbPath)) {
|
|
151
|
-
return { groups: [], generatedAt: new Date().toISOString(), note: 'state.db not found
|
|
151
|
+
return { groups: [], generatedAt: new Date().toISOString(), note: 'state.db not found — workspace may not be initialized' };
|
|
152
152
|
}
|
|
153
153
|
const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: true });
|
|
154
154
|
try {
|
|
@@ -161,7 +161,7 @@ export class ApprovalsGroupedConsoleModel {
|
|
|
161
161
|
}
|
|
162
162
|
catch (err) {
|
|
163
163
|
if (isMissingTableError(err)) {
|
|
164
|
-
return { groups: [], generatedAt: new Date().toISOString(), note: 'approval table not found
|
|
164
|
+
return { groups: [], generatedAt: new Date().toISOString(), note: 'approval table not found — workspace may not be initialized' };
|
|
165
165
|
}
|
|
166
166
|
throw err;
|
|
167
167
|
}
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"tagLowRisk": "Low Risk",
|
|
96
96
|
"recordCount": "{{count}} record(s)",
|
|
97
97
|
"deviationCount": "deviation(s)",
|
|
98
|
-
"evidenceSummary": "
|
|
98
|
+
"evidenceSummary": "Approval records: {{count}}",
|
|
99
99
|
"untitledCandidate": "Untitled candidate principle",
|
|
100
100
|
"promptCapacityUnconfirmed": "Host capacity unconfirmed: the applicable hosts of this workspace take different injection routes; the numbers below are not bound to a specific host.",
|
|
101
101
|
"promptCapacitySharedRoute": "shared injection (billed on the full injected block)",
|
|
@@ -107,18 +107,18 @@
|
|
|
107
107
|
"promptScopeShared": "Forecast window (shared route, billed on the full injected block, unit=chars)",
|
|
108
108
|
"promptScopeList": "Forecast window (list route, billed on the selected lines{{full}}, unit=chars)",
|
|
109
109
|
"promptScopeFullRender": "; full injected block ≈ {{count}} chars",
|
|
110
|
-
"artifactUnavailableNote": "The draft artifact this approval points to is no longer in the artifact store (likely superseded by a newer revision), so its content cannot be shown. If in doubt, reject it; the raw artifact ID is shown in the
|
|
111
|
-
"unlinkedCandidateNote": "No principle name is available for this card: its lineage cannot be resolved to a principle, and no readable title could be extracted from its stored content. Judge it from the
|
|
110
|
+
"artifactUnavailableNote": "The draft artifact this approval points to is no longer in the artifact store (likely superseded by a newer revision), so its content cannot be shown. If in doubt, reject it; the raw artifact ID is shown in the replacement section's current-artifact field.",
|
|
111
|
+
"unlinkedCandidateNote": "No principle name is available for this card: its lineage cannot be resolved to a principle, and no readable title could be extracted from its stored content. Judge it from the approval records below; if you cannot decide, you can reject it.",
|
|
112
112
|
"approveAction": "Approve",
|
|
113
113
|
"rejectAction": "Reject",
|
|
114
|
-
"editAction": "
|
|
114
|
+
"editAction": "Replace pending version",
|
|
115
115
|
"confirmReject": "Confirm reject",
|
|
116
|
-
"confirmEdit": "Confirm
|
|
116
|
+
"confirmEdit": "Confirm replacement",
|
|
117
117
|
"cancelAction": "Cancel",
|
|
118
118
|
"rejectReasonLabel": "Reject reason (required)",
|
|
119
119
|
"rejectReasonPlaceholder": "Explain why this is rejected",
|
|
120
|
-
"editReasonLabel": "
|
|
121
|
-
"editReasonPlaceholder": "Explain why
|
|
120
|
+
"editReasonLabel": "Replacement reason (required)",
|
|
121
|
+
"editReasonPlaceholder": "Explain why this pending version needs replacing",
|
|
122
122
|
"editNewArtifactPlaceholder": "Enter validated revision artifact ID",
|
|
123
123
|
"currentArtifactLabel": "Current artifact",
|
|
124
124
|
"approveSucceededCount": "Approved {{count}} principle(s)",
|
|
@@ -150,10 +150,10 @@
|
|
|
150
150
|
"overflowTitle": "{{count}} more warning(s)"
|
|
151
151
|
},
|
|
152
152
|
"rejectSucceeded": "Rejected",
|
|
153
|
-
"editSucceeded": "
|
|
153
|
+
"editSucceeded": "Pending version replaced",
|
|
154
154
|
"approveFailed": "Approval failed, please retry.",
|
|
155
155
|
"rejectFailed": "Rejection failed, please retry.",
|
|
156
|
-
"editFailed": "
|
|
156
|
+
"editFailed": "Replacement failed. Please try again later.",
|
|
157
157
|
"partialFailure": "Action completed, but {{failedCount}}/{{totalCount}} record(s) failed: {{reason}}",
|
|
158
158
|
"unknownFailure": "Check the service logs.",
|
|
159
159
|
"onboardingTitle": "How PD Works",
|
|
@@ -385,7 +385,8 @@
|
|
|
385
385
|
"formationDiagnosisLabel": "Diagnosis",
|
|
386
386
|
"formationRootCauseLabel": "Root cause",
|
|
387
387
|
"formationEvidenceLabel": "Supporting evidence",
|
|
388
|
-
"formationUnavailableNote": "The source diagnosis is currently unreadable; this principle is still decided on the evidence shown."
|
|
388
|
+
"formationUnavailableNote": "The source diagnosis is currently unreadable; this principle is still decided on the evidence shown.",
|
|
389
|
+
"checksDisclosure": "Deterministic checks (technical detail)"
|
|
389
390
|
},
|
|
390
391
|
"revisionReviewUnavailable": "Revision review is incomplete or multiple versions are pending. Check the artifacts and prior version before approval; rejection remains available.",
|
|
391
392
|
"revisionIntentReview": "Review the revised statement against the retained intent, including triggers, required actions, and exceptions.",
|
|
@@ -766,28 +767,28 @@
|
|
|
766
767
|
"approve": "Approve Principle",
|
|
767
768
|
"park": "Park",
|
|
768
769
|
"reject": "Reject",
|
|
769
|
-
"editAction": "
|
|
770
|
+
"editAction": "Replace pending version",
|
|
770
771
|
"confirmApprove": "After approval, the principle will activate via prompt channel. Confirm approval?",
|
|
771
772
|
"confirm": "Confirm",
|
|
772
773
|
"cancel": "Cancel",
|
|
773
774
|
"rejectReasonPlaceholder": "Please explain why you reject (required)",
|
|
774
775
|
"approveNoteLabel": "Approval note (optional)",
|
|
775
776
|
"approveNotePlaceholder": "Why you approve — recorded as the decision note (may be left blank)",
|
|
776
|
-
"editReasonLabel": "
|
|
777
|
-
"editReasonPlaceholder": "Explain why
|
|
777
|
+
"editReasonLabel": "Replacement reason (required)",
|
|
778
|
+
"editReasonPlaceholder": "Explain why this pending version needs replacing",
|
|
778
779
|
"editNewArtifactPlaceholder": "Enter validated revision artifact ID",
|
|
779
780
|
"currentArtifactLabel": "Current artifact",
|
|
780
781
|
"confirmReject": "Confirm Rejection",
|
|
781
|
-
"confirmEdit": "Confirm
|
|
782
|
+
"confirmEdit": "Confirm replacement",
|
|
782
783
|
"approved": "Approved / Will activate via prompt channel",
|
|
783
784
|
"rejected": "Rejected",
|
|
784
|
-
"editSucceeded": "
|
|
785
|
+
"editSucceeded": "Pending version replaced",
|
|
785
786
|
"parked": "Parked",
|
|
786
787
|
"viewActivation": "View activation status",
|
|
787
788
|
"approveFailed": "Approval failed. Please try again later.",
|
|
788
789
|
"partialFailure": "Decision applied, but {{failedCount}}/{{totalCount}} records failed: {{reason}}",
|
|
789
790
|
"rejectFailed": "Rejection failed. Please try again later.",
|
|
790
|
-
"editFailed": "
|
|
791
|
+
"editFailed": "Replacement failed. Please try again later.",
|
|
791
792
|
"unknownFailure": "Check the service logs.",
|
|
792
793
|
"decisionTitle": "Decision Actions",
|
|
793
794
|
"parkUnavailable": "Park is not yet available",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"tagLowRisk": "低风险",
|
|
96
96
|
"recordCount": "{{count}} 条记录",
|
|
97
97
|
"deviationCount": "条偏差",
|
|
98
|
-
"evidenceSummary": "
|
|
98
|
+
"evidenceSummary": "审批记录:{{count}} 条",
|
|
99
99
|
"untitledCandidate": "待命名候选原则",
|
|
100
100
|
"promptCapacityUnconfirmed": "当前宿主容量未确认:本工作区适用的宿主会走不同的注入路由,以下数字未绑定具体宿主。",
|
|
101
101
|
"promptCapacitySharedRoute": "共享注入(按完整注入块计费)",
|
|
@@ -107,18 +107,18 @@
|
|
|
107
107
|
"promptScopeShared": "预测窗口(共享注入路由,按完整注入块计费,单位=字符)",
|
|
108
108
|
"promptScopeList": "预测窗口(列表注入路由,按选中行计费{{full}},单位=字符)",
|
|
109
109
|
"promptScopeFullRender": ",完整注入块约 {{count}} 字符",
|
|
110
|
-
"artifactUnavailableNote": "
|
|
111
|
-
"unlinkedCandidateNote": "
|
|
110
|
+
"artifactUnavailableNote": "该审批指向的草稿原件已不在工件库中(可能已被新版本取代),内容无法显示。如无法判断,可选择拒绝;原始工件编号可点「替换待审版本」后在「当前工件」一栏查看。",
|
|
111
|
+
"unlinkedCandidateNote": "这张卡片没有可展示的原则名称:无法把它的血缘解析到某条原则,也没能从其存档内容里提取出可读的标题。请依据下方的审批记录判断;如无法判断,可选择拒绝。",
|
|
112
112
|
"approveAction": "批准",
|
|
113
113
|
"rejectAction": "拒绝",
|
|
114
|
-
"editAction": "
|
|
114
|
+
"editAction": "替换待审版本",
|
|
115
115
|
"confirmReject": "确认拒绝",
|
|
116
|
-
"confirmEdit": "
|
|
116
|
+
"confirmEdit": "确认替换",
|
|
117
117
|
"cancelAction": "取消",
|
|
118
118
|
"rejectReasonLabel": "拒绝原因(必填)",
|
|
119
119
|
"rejectReasonPlaceholder": "请说明拒绝原因",
|
|
120
|
-
"editReasonLabel": "
|
|
121
|
-
"editReasonPlaceholder": "
|
|
120
|
+
"editReasonLabel": "替换原因(必填)",
|
|
121
|
+
"editReasonPlaceholder": "请说明为什么需要替换这份待审版本",
|
|
122
122
|
"editNewArtifactPlaceholder": "输入新的已验证工件 ID",
|
|
123
123
|
"currentArtifactLabel": "当前工件",
|
|
124
124
|
"approveSucceededCount": "已批准 {{count}} 条原则",
|
|
@@ -150,10 +150,10 @@
|
|
|
150
150
|
"overflowTitle": "还有 {{count}} 条警告"
|
|
151
151
|
},
|
|
152
152
|
"rejectSucceeded": "已拒绝",
|
|
153
|
-
"editSucceeded": "
|
|
153
|
+
"editSucceeded": "已替换待审版本",
|
|
154
154
|
"approveFailed": "批准失败,请稍后重试。",
|
|
155
155
|
"rejectFailed": "拒绝失败,请稍后重试。",
|
|
156
|
-
"editFailed": "
|
|
156
|
+
"editFailed": "替换失败,请稍后重试。",
|
|
157
157
|
"partialFailure": "操作完成,但 {{failedCount}}/{{totalCount}} 条记录失败:{{reason}}",
|
|
158
158
|
"unknownFailure": "请检查服务日志。",
|
|
159
159
|
"onboardingTitle": "PD 的工作方式",
|
|
@@ -385,7 +385,8 @@
|
|
|
385
385
|
"formationDiagnosisLabel": "诊断",
|
|
386
386
|
"formationRootCauseLabel": "根因",
|
|
387
387
|
"formationEvidenceLabel": "支撑证据",
|
|
388
|
-
"formationUnavailableNote": "来源诊断当前不可读;这条原则仍按现有证据供你裁决。"
|
|
388
|
+
"formationUnavailableNote": "来源诊断当前不可读;这条原则仍按现有证据供你裁决。",
|
|
389
|
+
"checksDisclosure": "确定性检查(技术详情)"
|
|
389
390
|
},
|
|
390
391
|
"revisionReviewUnavailable": "修订对照信息不完整或同时存在多个待审版本,请先核对工件与旧版本;仍可拒绝本次修订。",
|
|
391
392
|
"revisionIntentReview": "修订复核:确认新正文保留触发条件、行动要求和必要例外,并与沿用意图一致。",
|
|
@@ -766,28 +767,28 @@
|
|
|
766
767
|
"approve": "批准原则",
|
|
767
768
|
"park": "暂存",
|
|
768
769
|
"reject": "拒绝",
|
|
769
|
-
"editAction": "
|
|
770
|
+
"editAction": "替换待审版本",
|
|
770
771
|
"confirmApprove": "批准后原则将通过提示词通道激活。确认批准?",
|
|
771
772
|
"confirm": "确认",
|
|
772
773
|
"cancel": "取消",
|
|
773
774
|
"rejectReasonPlaceholder": "请说明拒绝原因(必填)",
|
|
774
775
|
"approveNoteLabel": "批准备注(可选)",
|
|
775
776
|
"approveNotePlaceholder": "写下批准的理由,将作为裁决记录保存(可留空)",
|
|
776
|
-
"editReasonLabel": "
|
|
777
|
-
"editReasonPlaceholder": "
|
|
777
|
+
"editReasonLabel": "替换原因(必填)",
|
|
778
|
+
"editReasonPlaceholder": "请说明为什么需要替换这份待审版本",
|
|
778
779
|
"editNewArtifactPlaceholder": "输入新的已验证工件 ID",
|
|
779
780
|
"currentArtifactLabel": "当前工件",
|
|
780
781
|
"confirmReject": "确认拒绝",
|
|
781
|
-
"confirmEdit": "
|
|
782
|
+
"confirmEdit": "确认替换",
|
|
782
783
|
"approved": "已批准 / 将通过提示词通道激活",
|
|
783
784
|
"rejected": "已拒绝",
|
|
784
|
-
"editSucceeded": "
|
|
785
|
+
"editSucceeded": "已替换待审版本",
|
|
785
786
|
"parked": "已暂存",
|
|
786
787
|
"viewActivation": "查看生效情况",
|
|
787
788
|
"approveFailed": "批准失败,请稍后重试。",
|
|
788
789
|
"partialFailure": "决策已执行,但 {{failedCount}}/{{totalCount}} 条记录失败:{{reason}}",
|
|
789
790
|
"rejectFailed": "拒绝失败,请稍后重试。",
|
|
790
|
-
"editFailed": "
|
|
791
|
+
"editFailed": "替换失败,请稍后重试。",
|
|
791
792
|
"unknownFailure": "请检查服务日志。",
|
|
792
793
|
"decisionTitle": "决策操作",
|
|
793
794
|
"parkUnavailable": "暂存尚未可用",
|
|
@@ -10,6 +10,7 @@ import { DailyThoughtCard } from "../../components/focus/daily-thought-card.js";
|
|
|
10
10
|
import { ShinyText } from "../../components/ui/shiny-text.js";
|
|
11
11
|
import { fetchGovernanceQueue, fetchApprovalsGrouped, approveApproval, rejectApproval, editApproval, fetchAllActivations, fetchOwnerDecisions, } from "../../api.js";
|
|
12
12
|
import { selectApprovalGroupDisplayTitle, showsUnlinkedCandidateNote, validateApprovalsGroupedData } from "./focus-validation.js";
|
|
13
|
+
import { splitReadingTitleBody } from "../../utils/reading-layout.js";
|
|
13
14
|
import { OwnerDecisionCard } from "./OwnerDecisionCard.js";
|
|
14
15
|
import { localizeApprovalWarning, splitApprovalWarnings } from "../../utils/approval-warning-localization.js";
|
|
15
16
|
/** PRI-908: how long the decided card stays visible before the pending list refresh removes it. */
|
|
@@ -273,6 +274,9 @@ function PendingReviewCard({ group, actionsLockedReason, onDecisionApplied, prom
|
|
|
273
274
|
const displayTitle = selectApprovalGroupDisplayTitle(group)
|
|
274
275
|
?? t("pages.focus.untitledCandidate", { defaultValue: "待命名候选原则" });
|
|
275
276
|
const unlinkedCandidateNote = showsUnlinkedCandidateNote(group);
|
|
277
|
+
// SPEC-PD-CONSOLE-READABILITY-002 切片1(R2/R3/R7):候选描述可能是整段规则
|
|
278
|
+
// 正文——不再整段充当加粗标题。短标题只作预览,逐字全文由正文块承载。
|
|
279
|
+
const readingLayout = splitReadingTitleBody(displayTitle);
|
|
276
280
|
async function applyDecisionToAllRecords(action, reason) {
|
|
277
281
|
// Only process pending records — skip already-approved/rejected to avoid
|
|
278
282
|
// stable partial failures on mixed-status groups.
|
|
@@ -371,7 +375,7 @@ function PendingReviewCard({ group, actionsLockedReason, onDecisionApplied, prom
|
|
|
371
375
|
const { allSucceeded, failedCount, failureReason } = summarizeDecisionResults(results);
|
|
372
376
|
const totalCount = results.length;
|
|
373
377
|
if (allSucceeded) {
|
|
374
|
-
toast.success(t("pages.focus.editSucceeded", { defaultValue: "
|
|
378
|
+
toast.success(t("pages.focus.editSucceeded", { defaultValue: "已替换待审版本" }));
|
|
375
379
|
setShowEditInput(false);
|
|
376
380
|
setEditReason("");
|
|
377
381
|
setNewArtifactId("");
|
|
@@ -379,7 +383,7 @@ function PendingReviewCard({ group, actionsLockedReason, onDecisionApplied, prom
|
|
|
379
383
|
}
|
|
380
384
|
else {
|
|
381
385
|
toast.error(t("pages.focus.partialFailure", {
|
|
382
|
-
defaultValue:
|
|
386
|
+
defaultValue: `替换完成,但 ${failedCount}/${totalCount} 条记录失败。`,
|
|
383
387
|
failedCount,
|
|
384
388
|
totalCount,
|
|
385
389
|
reason: failureReason ?? t("pages.focus.unknownFailure", { defaultValue: "请检查服务日志。" }),
|
|
@@ -388,7 +392,7 @@ function PendingReviewCard({ group, actionsLockedReason, onDecisionApplied, prom
|
|
|
388
392
|
}
|
|
389
393
|
}
|
|
390
394
|
catch {
|
|
391
|
-
toast.error(t("pages.focus.editFailed", { defaultValue: "
|
|
395
|
+
toast.error(t("pages.focus.editFailed", { defaultValue: "替换失败,请稍后重试。" }));
|
|
392
396
|
}
|
|
393
397
|
finally {
|
|
394
398
|
setActionLoading(false);
|
|
@@ -398,7 +402,7 @@ function PendingReviewCard({ group, actionsLockedReason, onDecisionApplied, prom
|
|
|
398
402
|
? "border-green/40 bg-green/10 text-green"
|
|
399
403
|
: "border-line bg-surface/80 text-ink-3"}`, role: "status", "data-testid": `decided-banner-${group.principleId}`, children: decidedOutcome === "approved"
|
|
400
404
|
? t("pages.focus.decidedApproved", { defaultValue: "已批准 ✓ 正在写入生效记录…" })
|
|
401
|
-
: t("pages.focus.decidedRejected", { defaultValue: "已拒绝" }) })), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: channelLabel }), isReversible && (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: primaryChannel === "prompt" ? t("pages.focus.tagReversible") : t("pages.focus.tagLowRisk") }))] }), _jsx("div", { className: "mt-[14px] mb-2 font-semibold text-ink leading-snug", children:
|
|
405
|
+
: t("pages.focus.decidedRejected", { defaultValue: "已拒绝" }) })), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: channelLabel }), isReversible && (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: primaryChannel === "prompt" ? t("pages.focus.tagReversible") : t("pages.focus.tagLowRisk") }))] }), _jsx("div", { className: "mt-[14px] mb-2 font-semibold text-ink leading-snug", "data-testid": `candidate-title-${group.principleId}`, children: readingLayout.title }), readingLayout.body !== null && (_jsx("div", { className: "mb-2 text-[15px] leading-relaxed text-ink whitespace-pre-wrap break-words", "data-testid": `candidate-body-${group.principleId}`, children: readingLayout.body })), group.artifactUnavailable === true && (_jsx("div", { className: "mb-2 text-[12.5px] leading-relaxed text-amber", "data-testid": `artifact-unavailable-${group.principleId}`, children: t("pages.focus.artifactUnavailableNote") })), unlinkedCandidateNote && (_jsx("div", { className: "mb-2 text-[12.5px] leading-relaxed text-amber", "data-testid": `unlinked-candidate-${group.principleId}`, children: t("pages.focus.unlinkedCandidateNote") })), _jsx("div", { className: "mt-2 px-3 py-2 bg-surface/60 border-l-2 border-gov text-ink-3 text-[13px] leading-snug", children: t("pages.focus.evidenceSummary", { count: group.records.length }) }), actionsLocked && (_jsxs("div", { className: "mt-3 rounded-[3px] border border-amber/40 bg-amber/5 p-3", "data-testid": `pending-actions-locked-${group.principleId}`, children: [_jsx("p", { className: "text-ink-2 text-[12.5px] leading-relaxed", children: actionsLockedReason }), _jsx(Link, { to: "/settings", "data-testid": `pending-actions-locked-settings-${group.principleId}`, className: "mt-2 inline-flex items-center border border-line text-ink bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.ownerDecision.goSettingsCta") })] })), decidedOutcome === null && primaryChannel === "prompt" && promptInjection?.truncated === true && (_jsxs("div", { className: "mt-3 rounded-[3px] border border-amber/40 bg-amber/5 px-3 py-2 text-[12.5px] text-amber", "data-testid": `approve-queue-badge-${group.principleId}`, children: [promptInjection.productionRotates === true && group.fitsPromptBudget === true
|
|
402
406
|
? t("pages.focus.approveQueueBadgeRotation", {
|
|
403
407
|
defaultValue: "批准后可能排队:提示词注入位已满({{used}}/{{budget}} 字符)。当前按公平轮转选取,新原则会在后续轮次自行轮到,无需停用旧原则。",
|
|
404
408
|
used: promptInjection.usedChars,
|
|
@@ -420,7 +424,7 @@ function PendingReviewCard({ group, actionsLockedReason, onDecisionApplied, prom
|
|
|
420
424
|
full: promptInjection.fullRenderChars !== undefined
|
|
421
425
|
? t("pages.focus.promptScopeFullRender", { count: promptInjection.fullRenderChars })
|
|
422
426
|
: "",
|
|
423
|
-
}), promptInjection.hostKind !== undefined ? ` · ${promptInjection.hostKind === "openclaw" ? "OpenClaw" : promptInjection.hostKind === "workbuddy" ? "WorkBuddy" : "Codex"}` : ""] })] })), group.revisionReviewUnavailable === true && _jsx("p", { role: "alert", className: "mt-3 text-danger", children: t("pages.focus.revisionReviewUnavailable") }), group.revisionReview !== undefined && (_jsxs("div", { className: "mt-3 border border-line p-3 text-[12.5px]", "data-testid": `revision-intent-review-${group.principleId}`, children: [_jsx("p", { children: t("pages.focus.revisionIntentReview", { defaultValue: "修订复核:确认新正文保留触发条件、行动要求和必要例外,并与沿用意图一致。" }) }), _jsx("pre", { className: "mt-2 max-h-32 overflow-auto whitespace-pre-wrap", children: group.revisionReview.oldStatement }), _jsx("pre", { className: "mt-2 max-h-32 overflow-auto whitespace-pre-wrap", children: group.revisionReview.newStatement }), _jsx("dl", { className: "mt-2", children: Object.entries(group.revisionReview.intentFields).map(([key, value]) => _jsxs("div", { children: [_jsx("dt", { children: t(`pages.focus.intentField.${key}`, { defaultValue: key }) }), _jsx("dd", { children: value })] }, key)) }), _jsxs("label", { className: "mt-2 flex gap-2", children: [_jsx("input", { type: "checkbox", checked: intentReviewed, onChange: (event) => setIntentReviewed(event.target.checked) }), t("pages.focus.revisionIntentConfirmed", { defaultValue: "我已核对上述意图与新正文一致" })] })] })), _jsxs("div", { className: "flex gap-2 mt-4 flex-wrap items-center", children: [_jsx("button", { type: "button", onClick: handleApprove, disabled: decisionDisabled || (group.revisionReviewUnavailable === true || (group.revisionReview !== undefined && !intentReviewed)), "data-testid": `approve-btn-${group.principleId}`, className: "inline-flex items-center border border-gov bg-gov text-paper rounded-[3px] px-[14px] py-[6px] text-[12.5px] font-medium hover:bg-gov-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: actionLoading ? t("common.loading") + "…" : t("pages.focus.approveAction", { defaultValue: "批准" }) }), _jsx("button", { type: "button", onClick: () => { setShowEditInput((v) => !v); setShowRejectInput(false); }, disabled: decisionDisabled, "data-testid": `edit-btn-${group.principleId}`, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.editAction", { defaultValue: "
|
|
427
|
+
}), promptInjection.hostKind !== undefined ? ` · ${promptInjection.hostKind === "openclaw" ? "OpenClaw" : promptInjection.hostKind === "workbuddy" ? "WorkBuddy" : "Codex"}` : ""] })] })), group.revisionReviewUnavailable === true && _jsx("p", { role: "alert", className: "mt-3 text-danger", children: t("pages.focus.revisionReviewUnavailable") }), group.revisionReview !== undefined && (_jsxs("div", { className: "mt-3 border border-line p-3 text-[12.5px]", "data-testid": `revision-intent-review-${group.principleId}`, children: [_jsx("p", { children: t("pages.focus.revisionIntentReview", { defaultValue: "修订复核:确认新正文保留触发条件、行动要求和必要例外,并与沿用意图一致。" }) }), _jsx("pre", { className: "mt-2 max-h-32 overflow-auto whitespace-pre-wrap", children: group.revisionReview.oldStatement }), _jsx("pre", { className: "mt-2 max-h-32 overflow-auto whitespace-pre-wrap", children: group.revisionReview.newStatement }), _jsx("dl", { className: "mt-2", children: Object.entries(group.revisionReview.intentFields).map(([key, value]) => _jsxs("div", { children: [_jsx("dt", { children: t(`pages.focus.intentField.${key}`, { defaultValue: key }) }), _jsx("dd", { children: value })] }, key)) }), _jsxs("label", { className: "mt-2 flex gap-2", children: [_jsx("input", { type: "checkbox", checked: intentReviewed, onChange: (event) => setIntentReviewed(event.target.checked) }), t("pages.focus.revisionIntentConfirmed", { defaultValue: "我已核对上述意图与新正文一致" })] })] })), _jsxs("div", { className: "flex gap-2 mt-4 flex-wrap items-center", children: [_jsx("button", { type: "button", onClick: handleApprove, disabled: decisionDisabled || (group.revisionReviewUnavailable === true || (group.revisionReview !== undefined && !intentReviewed)), "data-testid": `approve-btn-${group.principleId}`, className: "inline-flex items-center border border-gov bg-gov text-paper rounded-[3px] px-[14px] py-[6px] text-[12.5px] font-medium hover:bg-gov-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: actionLoading ? t("common.loading") + "…" : t("pages.focus.approveAction", { defaultValue: "批准" }) }), _jsx("button", { type: "button", onClick: () => { setShowEditInput((v) => !v); setShowRejectInput(false); }, disabled: decisionDisabled, "data-testid": `edit-btn-${group.principleId}`, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.editAction", { defaultValue: "替换待审版本" }) }), _jsx("button", { type: "button", onClick: () => { setShowRejectInput((v) => !v); setShowEditInput(false); }, disabled: decisionDisabled, "data-testid": `reject-btn-${group.principleId}`, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.rejectAction", { defaultValue: "拒绝" }) }), _jsx(Link, { to: "/pain", className: "inline-flex items-center text-gov text-[12.5px] hover:underline focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: t("pages.focus.viewFullChain") })] }), showRejectInput && (_jsxs("div", { className: "mt-3 p-3 border border-danger/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("pages.focus.rejectReasonLabel", { defaultValue: "拒绝原因(必填)" }) }), _jsx("textarea", { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), "data-testid": `reject-reason-${group.principleId}`, placeholder: t("pages.focus.rejectReasonPlaceholder", { defaultValue: "请说明拒绝原因" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx("button", { type: "button", onClick: handleReject, disabled: !rejectReason.trim() || actionLoading, "data-testid": `confirm-reject-${group.principleId}`, className: "inline-flex items-center border border-danger text-danger bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-danger/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.focus.confirmReject", { defaultValue: "确认拒绝" }) }), _jsx("button", { type: "button", onClick: () => { setShowRejectInput(false); setRejectReason(""); }, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.cancelAction", { defaultValue: "取消" }) })] })] })), showEditInput && (_jsxs("div", { className: "mt-3 p-3 border border-gov/20 rounded-[var(--radius-md)]", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("pages.focus.editReasonLabel", { defaultValue: "替换原因(必填)" }) }), _jsxs("div", { className: "mb-2 text-ink-4 text-[12px] font-mono", children: [t("pages.focus.currentArtifactLabel", { defaultValue: "当前工件" }), ": ", currentArtifactId] }), _jsx("input", { type: "text", value: newArtifactId, onChange: (e) => setNewArtifactId(e.target.value), "data-testid": `edit-new-artifact-${group.principleId}`, placeholder: t("pages.focus.editNewArtifactPlaceholder", { defaultValue: "输入新的已验证工件 ID" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] mb-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov", "aria-label": t("pages.focus.editNewArtifactPlaceholder", { defaultValue: "输入新的已验证工件 ID" }) }), _jsx("textarea", { value: editReason, onChange: (e) => setEditReason(e.target.value), "data-testid": `edit-reason-${group.principleId}`, placeholder: t("pages.focus.editReasonPlaceholder", { defaultValue: "请说明为什么需要替换这份待审版本" }), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[80px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" }), _jsxs("div", { className: "flex gap-2 mt-2", children: [_jsx("button", { type: "button", onClick: handleEdit, disabled: !editReason.trim() || !newArtifactId.trim() || actionLoading, "data-testid": `confirm-edit-${group.principleId}`, className: "inline-flex items-center border border-gov text-gov bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-gov/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: t("pages.focus.confirmEdit", { defaultValue: "确认替换" }) }), _jsx("button", { type: "button", onClick: () => { setShowEditInput(false); setEditReason(""); setNewArtifactId(""); }, className: "inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.cancelAction", { defaultValue: "取消" }) })] })] }))] }));
|
|
424
428
|
}
|
|
425
429
|
function StagnationSignalCard({ signal }) {
|
|
426
430
|
const { t } = useTranslation();
|
|
@@ -95,7 +95,7 @@ export function OwnerDecisionCard({ item, onResolved, governanceReady = true, ac
|
|
|
95
95
|
: item.kind === "rulecode_decision"
|
|
96
96
|
? t("pages.focus.ownerDecision.kindRulecode")
|
|
97
97
|
: t("pages.focus.ownerDecision.kindEvaluator");
|
|
98
|
-
return (_jsxs("article", { className: "bg-panel border border-line rounded-[6px] p-4 mb-3", "data-testid": `owner-decision-${item.taskId}`, children: [_jsxs("div", { className: "flex items-start justify-between gap-3 mb-2", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-ink text-[14px] font-medium leading-snug", children: item.title }), _jsx("div", { className: "mt-1 text-ink-4 text-[11px] font-mono uppercase tracking-[0.08em]", children: kindLabel })] }), item.legacy && (_jsx("span", { className: "shrink-0 text-ink-4 border border-line rounded-[2px] px-[7px] py-[3px] text-[10.5px] font-mono", children: t("pages.focus.ownerDecision.legacyBadge") }))] }), _jsx("p", { className: "text-ink-2 text-[13px] leading-relaxed mb-2", children: item.summary }), item.review?.brief.kind === "evaluator" && (_jsxs("div", { className: "mb-3 grid gap-2 text-[12.5px] leading-relaxed", children: [item.review.brief.principle?.statement && (_jsxs("p", { children: [_jsx("span", { className: "font-medium text-ink", children: t("pages.focus.ownerDecision.principleLabel") }), " ", item.review.brief.principle.statement] })), item.review.brief.implementation?.summary && (_jsxs("p", { children: [_jsx("span", { className: "font-medium text-ink", children: t("pages.focus.ownerDecision.implementationLabel") }), " ", item.review.brief.implementation.summary] })), (item.review.brief.concerns?.length ?? 0) > 0 && (_jsxs("p", { className: "text-amber", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.concernLabel") }), " ", item.review.brief.concerns?.[0]] })), (item.review.brief.qualityChecklist?.items?.length ?? 0) > 0 && (_jsxs("div", { className: "mt-1 rounded-[3px] border border-line bg-surface-2 p-2", "data-testid": `owner-quality-checklist-${item.taskId}`, children: [_jsx("div", { className: "font-mono text-[10.5px] uppercase tracking-[0.06em] text-ink-3 mb-1", children: t("pages.focus.ownerDecision.qualityChecklistLabel") }), _jsx("ul", { className: "grid gap-0.5", children: item.review.brief.qualityChecklist?.items.map((qItem) => (_jsxs("li", { className: "flex items-start gap-1.5 text-[12px]", children: [_jsx("span", { className: qItem.pass ? "text-emerald" : "text-amber", "aria-hidden": "true", children: qItem.pass ? "✓" : "!" }), _jsx("span", { className: "sr-only", children: qItem.pass ? t("pages.focus.ownerDecision.qualityCheckStatus.passed") : t("pages.focus.ownerDecision.qualityCheckStatus.failed") }), _jsxs("span", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t(`pages.focus.ownerDecision.qualityCheck.${qItem.id}`) }), " ", qItem.note] })] }, qItem.id))) })] })), item.review.brief.formationEvidence && (_jsxs("div", { className: "mt-1 rounded-[3px] border border-line bg-surface-2 p-2", "data-testid": `owner-formation-evidence-${item.taskId}`, children: [_jsx("div", { className: "font-mono text-[10.5px] uppercase tracking-[0.06em] text-ink-3 mb-1", children: t("pages.focus.ownerDecision.formationLabel") }), item.review.brief.formationEvidence.sourcePainId ? (_jsxs("p", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.formationPainLabel") }), " ", _jsx("span", { className: "font-mono text-[11.5px]", children: item.review.brief.formationEvidence.sourcePainId })] })) : null, item.review.brief.formationEvidence.diagnosis?.summary && (_jsxs("p", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.formationDiagnosisLabel") }), " ", item.review.brief.formationEvidence.diagnosis.summary] })), item.review.brief.formationEvidence.diagnosis?.rootCause && (_jsxs("p", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.formationRootCauseLabel") }), " ", item.review.brief.formationEvidence.diagnosis.rootCause] })), (item.review.brief.formationEvidence.diagnosis?.evidence.length ?? 0) > 0 && (_jsxs("div", { children: [_jsx("span", { className: "font-medium text-ink-2", children: t("pages.focus.ownerDecision.formationEvidenceLabel") }), _jsx("ul", { className: "mt-0.5 grid gap-0.5 text-[12px] text-ink-3", children: item.review.brief.formationEvidence.diagnosis?.evidence.slice(0, 3).map((row, index) => (_jsxs("li", { children: [_jsx("span", { className: "font-mono text-[11px]", children: row.sourceRef }), " ", row.note] }, `${row.sourceRef}-${index}`))) })] })), !item.review.brief.formationEvidence.diagnosis && (_jsx("p", { className: "text-ink-3", children: t("pages.focus.ownerDecision.formationUnavailableNote") }))] }))] })), item.review && (_jsxs("div", { className: "mb-2 flex flex-wrap gap-2 text-[10.5px] font-mono uppercase tracking-[0.06em]", children: [_jsx("span", { className: "rounded-[2px] border border-line px-2 py-1 text-ink-3", children: t(`pages.focus.ownerDecision.evidence.${item.review.evidence.completeness}`) }), item.review.evidence.deterministicChecks.map(check => (_jsxs("span", { className: "rounded-[2px] border border-line px-2 py-1 text-ink-3", children: [check.check, ": ", check.status] }, check.check)))] })), item.machineRecommendation && (_jsxs("p", { className: "text-ink-3 text-[12.5px] mb-1", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.machineRecommendationLabel") }), " ", item.machineRecommendation, item.score !== undefined && _jsx("span", { className: "font-mono ml-1", children: item.score.toFixed(2) })] })), item.kind === "rollout_review" && (_jsx("p", { className: "text-ink-4 text-[12px] mb-1", children: t("pages.focus.ownerDecision.rolloutSafetyNote") })), evidenceUnavailable ? (_jsxs("div", { className: "mt-3 rounded-[3px] border border-amber/40 bg-amber/5 p-3", children: [_jsx("p", { className: "text-ink-2 text-[12.5px] leading-relaxed", children: t("pages.focus.ownerDecision.evidenceUnavailable") }), _jsx(Link, { to: `/failed-tasks?taskId=${encodeURIComponent(item.taskId)}`, "data-testid": `owner-evidence-recover-${item.taskId}`, className: "mt-2 inline-flex items-center border border-line text-ink bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.ownerDecision.goRecoverCta") })] })) : isTaskDecision ? (_jsxs(_Fragment, { children: [showInstruction && (_jsxs("div", { className: "mt-2 mb-2", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("pages.focus.ownerDecision.instructionLabel") }), _jsx("textarea", { value: instruction, onChange: (e) => setInstruction(e.target.value.slice(0, 500)), "data-testid": `owner-instruction-${item.taskId}`, placeholder: t("pages.focus.ownerDecision.instructionPlaceholder"), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[64px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" })] })), requiresPartialAcknowledgement && (_jsxs("label", { className: "mt-2 mb-1 flex items-start gap-2 text-[12px] text-ink-3", children: [_jsx("input", { type: "checkbox", checked: partialEvidenceAcknowledged, onChange: (event) => setPartialEvidenceAcknowledged(event.target.checked), "data-testid": `owner-partial-evidence-ack-${item.taskId}`, className: "mt-0.5" }), _jsx("span", { children: t("pages.focus.ownerDecision.partialEvidenceAcknowledgement") })] })), !governanceReady && actionsLockedReason && (_jsxs("div", { className: "mt-3 rounded-[3px] border border-amber/40 bg-amber/5 p-3", "data-testid": `owner-actions-locked-${item.taskId}`, children: [_jsx("p", { className: "text-ink-2 text-[12.5px] leading-relaxed", children: actionsLockedReason }), _jsx(Link, { to: "/settings", "data-testid": `owner-actions-locked-settings-${item.taskId}`, className: "mt-2 inline-flex items-center border border-line text-ink bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.ownerDecision.goSettingsCta") })] })), _jsxs("div", { className: "flex flex-wrap gap-2 mt-3", children: [canAccept && (_jsx("button", { type: "button", onClick: () => handleAction("accept_current"), disabled: !governanceReady || actionLoading !== null || (requiresPartialAcknowledgement && !partialEvidenceAcknowledged), "data-testid": `owner-accept-${item.taskId}`, className: "inline-flex items-center border border-gov text-gov bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-gov/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: actionLoading === "accept_current" ? t("common.loading") : t("pages.focus.ownerDecision.acceptCurrent") })), canRevise && (_jsx("button", { type: "button", onClick: () => {
|
|
98
|
+
return (_jsxs("article", { className: "bg-panel border border-line rounded-[6px] p-4 mb-3", "data-testid": `owner-decision-${item.taskId}`, children: [_jsxs("div", { className: "flex items-start justify-between gap-3 mb-2", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-ink text-[14px] font-medium leading-snug", children: item.title }), _jsx("div", { className: "mt-1 text-ink-4 text-[11px] font-mono uppercase tracking-[0.08em]", children: kindLabel })] }), item.legacy && (_jsx("span", { className: "shrink-0 text-ink-4 border border-line rounded-[2px] px-[7px] py-[3px] text-[10.5px] font-mono", children: t("pages.focus.ownerDecision.legacyBadge") }))] }), _jsx("p", { className: "text-ink-2 text-[13px] leading-relaxed mb-2", children: item.summary }), item.review?.brief.kind === "evaluator" && (_jsxs("div", { className: "mb-3 grid gap-2 text-[12.5px] leading-relaxed", children: [item.review.brief.principle?.statement && (_jsxs("p", { children: [_jsx("span", { className: "font-medium text-ink", children: t("pages.focus.ownerDecision.principleLabel") }), " ", item.review.brief.principle.statement] })), item.review.brief.implementation?.summary && (_jsxs("p", { children: [_jsx("span", { className: "font-medium text-ink", children: t("pages.focus.ownerDecision.implementationLabel") }), " ", item.review.brief.implementation.summary] })), (item.review.brief.concerns?.length ?? 0) > 0 && (_jsxs("p", { className: "text-amber", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.concernLabel") }), " ", item.review.brief.concerns?.[0]] })), (item.review.brief.qualityChecklist?.items?.length ?? 0) > 0 && (_jsxs("div", { className: "mt-1 rounded-[3px] border border-line bg-surface-2 p-2", "data-testid": `owner-quality-checklist-${item.taskId}`, children: [_jsx("div", { className: "font-mono text-[10.5px] uppercase tracking-[0.06em] text-ink-3 mb-1", children: t("pages.focus.ownerDecision.qualityChecklistLabel") }), _jsx("ul", { className: "grid gap-0.5", children: item.review.brief.qualityChecklist?.items.map((qItem) => (_jsxs("li", { className: "flex items-start gap-1.5 text-[12px]", children: [_jsx("span", { className: qItem.pass ? "text-emerald" : "text-amber", "aria-hidden": "true", children: qItem.pass ? "✓" : "!" }), _jsx("span", { className: "sr-only", children: qItem.pass ? t("pages.focus.ownerDecision.qualityCheckStatus.passed") : t("pages.focus.ownerDecision.qualityCheckStatus.failed") }), _jsxs("span", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t(`pages.focus.ownerDecision.qualityCheck.${qItem.id}`) }), " ", qItem.note] })] }, qItem.id))) })] })), item.review.brief.formationEvidence && (_jsxs("div", { className: "mt-1 rounded-[3px] border border-line bg-surface-2 p-2", "data-testid": `owner-formation-evidence-${item.taskId}`, children: [_jsx("div", { className: "font-mono text-[10.5px] uppercase tracking-[0.06em] text-ink-3 mb-1", children: t("pages.focus.ownerDecision.formationLabel") }), item.review.brief.formationEvidence.sourcePainId ? (_jsxs("p", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.formationPainLabel") }), " ", _jsx("span", { className: "font-mono text-[11.5px]", children: item.review.brief.formationEvidence.sourcePainId })] })) : null, item.review.brief.formationEvidence.diagnosis?.summary && (_jsxs("p", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.formationDiagnosisLabel") }), " ", item.review.brief.formationEvidence.diagnosis.summary] })), item.review.brief.formationEvidence.diagnosis?.rootCause && (_jsxs("p", { className: "text-ink-2", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.formationRootCauseLabel") }), " ", item.review.brief.formationEvidence.diagnosis.rootCause] })), (item.review.brief.formationEvidence.diagnosis?.evidence.length ?? 0) > 0 && (_jsxs("div", { children: [_jsx("span", { className: "font-medium text-ink-2", children: t("pages.focus.ownerDecision.formationEvidenceLabel") }), _jsx("ul", { className: "mt-0.5 grid gap-0.5 text-[12px] text-ink-3", children: item.review.brief.formationEvidence.diagnosis?.evidence.slice(0, 3).map((row, index) => (_jsxs("li", { children: [_jsx("span", { className: "font-mono text-[11px]", children: row.sourceRef }), " ", row.note] }, `${row.sourceRef}-${index}`))) })] })), !item.review.brief.formationEvidence.diagnosis && (_jsx("p", { className: "text-ink-3", children: t("pages.focus.ownerDecision.formationUnavailableNote") }))] }))] })), item.review && (_jsxs("div", { className: "mb-2 flex flex-wrap items-center gap-2 text-[10.5px] font-mono uppercase tracking-[0.06em]", children: [_jsx("span", { className: "rounded-[2px] border border-line px-2 py-1 text-ink-3", children: t(`pages.focus.ownerDecision.evidence.${item.review.evidence.completeness}`) }), _jsxs("details", { "data-testid": `owner-checks-${item.taskId}`, children: [_jsx("summary", { className: "cursor-pointer text-ink-4 hover:text-ink-3", children: t("pages.focus.ownerDecision.checksDisclosure") }), _jsx("div", { className: "mt-1 flex flex-wrap gap-2", children: item.review.evidence.deterministicChecks.map(check => (_jsxs("span", { className: "rounded-[2px] border border-line px-2 py-1 text-ink-3", children: [check.check, ": ", check.status] }, check.check))) })] })] })), item.machineRecommendation && (_jsxs("p", { className: "text-ink-3 text-[12.5px] mb-1", children: [_jsx("span", { className: "font-medium", children: t("pages.focus.ownerDecision.machineRecommendationLabel") }), " ", item.machineRecommendation, item.score !== undefined && _jsx("span", { className: "font-mono ml-1", children: item.score.toFixed(2) })] })), item.kind === "rollout_review" && (_jsx("p", { className: "text-ink-4 text-[12px] mb-1", children: t("pages.focus.ownerDecision.rolloutSafetyNote") })), evidenceUnavailable ? (_jsxs("div", { className: "mt-3 rounded-[3px] border border-amber/40 bg-amber/5 p-3", children: [_jsx("p", { className: "text-ink-2 text-[12.5px] leading-relaxed", children: t("pages.focus.ownerDecision.evidenceUnavailable") }), _jsx(Link, { to: `/failed-tasks?taskId=${encodeURIComponent(item.taskId)}`, "data-testid": `owner-evidence-recover-${item.taskId}`, className: "mt-2 inline-flex items-center border border-line text-ink bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.ownerDecision.goRecoverCta") })] })) : isTaskDecision ? (_jsxs(_Fragment, { children: [showInstruction && (_jsxs("div", { className: "mt-2 mb-2", children: [_jsx("label", { className: "block font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3 mb-2", children: t("pages.focus.ownerDecision.instructionLabel") }), _jsx("textarea", { value: instruction, onChange: (e) => setInstruction(e.target.value.slice(0, 500)), "data-testid": `owner-instruction-${item.taskId}`, placeholder: t("pages.focus.ownerDecision.instructionPlaceholder"), className: "w-full border border-line rounded-[var(--radius-md)] bg-surface text-ink px-3 py-2 text-[13px] min-h-[64px] resize-y focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov" })] })), requiresPartialAcknowledgement && (_jsxs("label", { className: "mt-2 mb-1 flex items-start gap-2 text-[12px] text-ink-3", children: [_jsx("input", { type: "checkbox", checked: partialEvidenceAcknowledged, onChange: (event) => setPartialEvidenceAcknowledged(event.target.checked), "data-testid": `owner-partial-evidence-ack-${item.taskId}`, className: "mt-0.5" }), _jsx("span", { children: t("pages.focus.ownerDecision.partialEvidenceAcknowledgement") })] })), !governanceReady && actionsLockedReason && (_jsxs("div", { className: "mt-3 rounded-[3px] border border-amber/40 bg-amber/5 p-3", "data-testid": `owner-actions-locked-${item.taskId}`, children: [_jsx("p", { className: "text-ink-2 text-[12.5px] leading-relaxed", children: actionsLockedReason }), _jsx(Link, { to: "/settings", "data-testid": `owner-actions-locked-settings-${item.taskId}`, className: "mt-2 inline-flex items-center border border-line text-ink bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors", children: t("pages.focus.ownerDecision.goSettingsCta") })] })), _jsxs("div", { className: "flex flex-wrap gap-2 mt-3", children: [canAccept && (_jsx("button", { type: "button", onClick: () => handleAction("accept_current"), disabled: !governanceReady || actionLoading !== null || (requiresPartialAcknowledgement && !partialEvidenceAcknowledged), "data-testid": `owner-accept-${item.taskId}`, className: "inline-flex items-center border border-gov text-gov bg-surface rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-gov/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: actionLoading === "accept_current" ? t("common.loading") : t("pages.focus.ownerDecision.acceptCurrent") })), canRevise && (_jsx("button", { type: "button", onClick: () => {
|
|
99
99
|
if (!showInstruction) {
|
|
100
100
|
setShowInstruction(true);
|
|
101
101
|
return;
|
|
@@ -12,6 +12,7 @@ import { ReceiptCoverageDisclosure, getReceiptSourceStatusLabelKey } from "../..
|
|
|
12
12
|
import { formatDate } from "../../utils/format-date.js";
|
|
13
13
|
import { localizeAggregatedBlocker } from "../../utils/blocker-localization.js";
|
|
14
14
|
import { groupByReasonCode, formatGroupedReasonLine } from "../../utils/attention-presentation.js";
|
|
15
|
+
import { splitReadingTitleBody } from "../../utils/reading-layout.js";
|
|
15
16
|
// ── Runtime validation (H section) ──────────────────────────────────────────
|
|
16
17
|
function isString(v) {
|
|
17
18
|
return typeof v === "string";
|
|
@@ -459,16 +460,21 @@ export function PrincipleDetailPage() {
|
|
|
459
460
|
const receiptPresentation = receipts?.status === 'ok'
|
|
460
461
|
? getReceiptPresentation(receipts.effectCount, receipts.presenceCount)
|
|
461
462
|
: null;
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
463
|
+
// 切片1(R2/R3/R7):标题/正文分层——h1 只承载短标题预览;known 路径的逐字
|
|
464
|
+
// 全文在下方 learned 块渲染(一次),unknown 路径在此处正文块渲染(一次)。
|
|
465
|
+
const learnedText = ownerDecision?.learnedPrinciple.status === 'known'
|
|
466
|
+
? ownerDecision.learnedPrinciple.value.text
|
|
467
|
+
: undefined;
|
|
468
|
+
const learnedKnown = learnedText !== undefined;
|
|
469
|
+
const headlineLayout = splitReadingTitleBody(learnedText ?? principle.text);
|
|
470
|
+
return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsxs(Button, { variant: "ghost", onClick: () => navigate("/principles"), className: "mb-4 -ml-2", children: ["\u2190 ", t("principles.detail.backToList")] }), _jsxs("section", { className: "mb-8", children: [_jsx(SectionTitle, { children: t("principles.detail.conclusion") }), _jsx("h1", { className: "text-[22px] font-semibold text-ink leading-snug mb-3", "data-testid": "principle-detail-title", children: headlineLayout.title }), headlineLayout.body !== null && !learnedKnown && (_jsx("div", { className: "mb-3 text-[15px] leading-relaxed text-ink whitespace-pre-wrap break-words", "data-testid": "principle-detail-body", children: headlineLayout.body })), ownerDecision !== null && ownerDecision.learnedPrinciple.status !== 'known' && (_jsx("p", { className: "text-amber text-[13px] leading-relaxed mb-3", "data-testid": "owner-decision-technical-note", children: t("principles.detail.ownerDecision.technicalOriginalNote", {
|
|
465
471
|
defaultValue: "以上为原始技术建议文本;可读的行为准则暂缺(保留原文以保证可追溯)。",
|
|
466
472
|
}) })), _jsx("p", { className: "text-ink-3 text-[14px] leading-relaxed mb-3", children: t("principles.detail.policyNote") }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: true, children: t("principles.detail.modifyWording") }), _jsx("span", { className: "text-ink-4 text-[13px]", children: t("principles.detail.modifyWordingNote") }), _jsx(Button, { variant: "ghost", size: "sm", className: "ml-auto", "data-testid": "principle-feedback", onClick: () => {
|
|
467
473
|
const params = new URLSearchParams({ source: "principle_page", principleId: id ?? "" });
|
|
468
474
|
navigate(`/report-problem?${params.toString()}`);
|
|
469
475
|
}, children: t("reportProblem.entryFeedback") })] })] }), ownerDecision !== null && (_jsxs("section", { className: "mb-8", "aria-labelledby": "owner-decision-title", "data-testid": "owner-decision-view", children: [_jsx(SectionTitle, { children: t("principles.detail.ownerDecision.title", { defaultValue: "这条原则的决策视图" }) }), _jsxs("div", { className: "rounded-[var(--radius-md)] border border-gov/25 bg-gov/5 p-4", children: [_jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between", children: [_jsxs("div", { children: [_jsx("h2", { id: "owner-decision-title", className: "text-[18px] font-semibold text-ink", children: t(DECISION_STATE_LABEL_KEY[ownerDecision.decisionState]) }), _jsx("p", { className: "mt-1 text-[13px] leading-relaxed text-ink-2", children: localizeNextAction(ownerDecision.nextAction.code, ownerDecision.nextAction.ownerText, t) })] }), _jsx("span", { "data-testid": "owner-decision-state", className: "w-fit rounded-full border border-line px-2 py-1 font-mono text-[11px] text-ink-3", children: ownerDecision.sourceReadStatus === 'complete'
|
|
470
476
|
? t("principles.detail.ownerDecision.readsComplete", { defaultValue: "本次所需数据已读取" })
|
|
471
|
-
: t("principles.detail.ownerDecision.readsPartial", { defaultValue: "部分数据源读取失败" }) })] }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-incident", label: t("principles.detail.ownerDecision.incident", { defaultValue: "发生了什么" }), field: ownerDecision.incidentSummary }), _jsxs("div", { className: "mt-4", "data-testid": "owner-decision-learned", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.learned", { defaultValue: "Agent 学到的行为准则" }) }), ownerDecision.learnedPrinciple.status === 'known' ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "mt-1 text-ink-2 text-[
|
|
477
|
+
: t("principles.detail.ownerDecision.readsPartial", { defaultValue: "部分数据源读取失败" }) })] }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-incident", label: t("principles.detail.ownerDecision.incident", { defaultValue: "发生了什么" }), field: ownerDecision.incidentSummary }), _jsxs("div", { className: "mt-4", "data-testid": "owner-decision-learned", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.learned", { defaultValue: "Agent 学到的行为准则" }) }), ownerDecision.learnedPrinciple.status === 'known' ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "mt-1 text-ink-2 text-[15px] leading-relaxed whitespace-pre-wrap break-words", children: learnedText }), _jsx("p", { className: "mt-1 text-ink-4 text-[12px]", children: t("principles.detail.ownerDecision.sourceTierLabel", {
|
|
472
478
|
defaultValue: "来源:{{tier}}(未经改写)",
|
|
473
479
|
tier: t(`principles.detail.ownerDecision.tier.${ownerDecision.learnedPrinciple.value.sourceTier}`, { defaultValue: ownerDecision.learnedPrinciple.value.sourceTier }),
|
|
474
480
|
}) })] })) : (_jsx("p", { className: "mt-1 text-amber text-[13px] leading-relaxed", children: ownerDecision.learnedPrinciple.reason.ownerText }))] }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-rationale", label: t("principles.detail.ownerDecision.rationale", { defaultValue: "为什么这么判断" }), field: ownerDecision.rationale }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-applicability", label: t("principles.detail.ownerDecision.applicability", { defaultValue: "什么时候适用" }), field: ownerDecision.applicability }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-expected", label: t("principles.detail.ownerDecision.expected", { defaultValue: "批准后会怎样(拟议行为,未部署)" }), field: ownerDecision.expectedBehavior }), ownerDecision.currentEnforcement.status === 'known' && (_jsxs("div", { className: "mt-4", "data-testid": "owner-decision-enforcement", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.enforcementLabel", { defaultValue: "当前实际执行" }) }), _jsx("p", { className: "mt-1 text-ink-2 text-[14px] leading-relaxed", children: ownerDecision.currentEnforcement.value?.state === 'active'
|
|
@@ -309,7 +309,7 @@ export function PrinciplesPage() {
|
|
|
309
309
|
STATUS_TEXT[card.status] +
|
|
310
310
|
" border-current/20", children: t("principles.status" + card.status.charAt(0).toUpperCase() + card.status.slice(1)) }), card.channels.map((ch) => (_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] border border-line rounded-[2px] px-2 py-0.5 text-ink-3", children: enumLabel('channel', ch, t) }, ch))), card.detectedLanguage && card.detectedLanguage !== 'unknown' && (_jsx("span", { className: "font-mono text-[11px] tracking-[0.02em] border border-line rounded-[2px] px-2 py-0.5 text-ink-4", children: card.detectedLanguage === 'zh'
|
|
311
311
|
? t("principles.languageHintZh")
|
|
312
|
-
: t("principles.languageHintEn") })), _jsxs("span", { className: "font-mono text-[11px] text-ink-4", children: [t("principles.evaluabilityLabel", { defaultValue: "可评价性" }), ": ", enumLabel('evaluability', card.evaluability, t)] }), card.decisionState !== undefined && (_jsx("span", { className: "font-mono text-[11px] tracking-[0.02em] border border-line rounded-[2px] px-2 py-0.5 text-ink-4", children: t(`principles.ownerDecision.state.${card.decisionState}`, { defaultValue: card.decisionState }) }))] }), card.readabilityWarningCode && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-amber/5 border border-amber/20 rounded-[3px] text-ink-3 text-[12px] leading-snug", children: t("principles.readabilityWarning." + card.readabilityWarningCode, { defaultValue: t("principles.readabilityFallbackTitle") }) })), card.detectedLanguage && card.detectedLanguage !== 'unknown' && i18n.language && !i18n.language.startsWith(card.detectedLanguage) && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-surface/60 border-l-2 border-line rounded-[3px] text-ink-4 text-[12px] leading-snug", children: t("principles.languageMismatchHint", { lang: card.detectedLanguage === 'en' ? 'English' : '中文' }) })), _jsx("h3", { className: "font-semibold text-ink mb-1", children: card.title }), card.titleUsedFallback && card.originalTitle && (_jsxs("details", { className: "mb-1", children: [_jsx("summary", { className: "text-ink-4 text-[11px] font-mono cursor-pointer hover:underline", children: t("principles.readabilityOriginalLabel") }), _jsx("div", { className: "text-ink-4 text-[12px] leading-snug mt-1 font-mono bg-surface/40 px-2 py-1 rounded-[3px] break-all", children: card.originalTitle })] })), _jsx("p", { className: "text-ink-3 text-[13px] line-clamp-3 leading-relaxed", children: card.text }), _jsxs("p", { className: "font-mono text-[11px] text-ink-4 mt-2", children: [t("principles.updatedAt"), ": ", formatDate(card.updatedAt, i18n.language)] }), _jsx(EvolutionBlock, { label: t("principles.blockBasis"), children:
|
|
312
|
+
: t("principles.languageHintEn") })), _jsxs("span", { className: "font-mono text-[11px] text-ink-4", children: [t("principles.evaluabilityLabel", { defaultValue: "可评价性" }), ": ", enumLabel('evaluability', card.evaluability, t)] }), card.decisionState !== undefined && (_jsx("span", { className: "font-mono text-[11px] tracking-[0.02em] border border-line rounded-[2px] px-2 py-0.5 text-ink-4", children: t(`principles.ownerDecision.state.${card.decisionState}`, { defaultValue: card.decisionState }) }))] }), card.readabilityWarningCode && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-amber/5 border border-amber/20 rounded-[3px] text-ink-3 text-[12px] leading-snug", children: t("principles.readabilityWarning." + card.readabilityWarningCode, { defaultValue: t("principles.readabilityFallbackTitle") }) })), card.detectedLanguage && card.detectedLanguage !== 'unknown' && i18n.language && !i18n.language.startsWith(card.detectedLanguage) && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-surface/60 border-l-2 border-line rounded-[3px] text-ink-4 text-[12px] leading-snug", children: t("principles.languageMismatchHint", { lang: card.detectedLanguage === 'en' ? 'English' : '中文' }) })), _jsx("h3", { className: "font-semibold text-ink mb-1", children: card.title }), card.titleUsedFallback && card.originalTitle && (_jsxs("details", { className: "mb-1", children: [_jsx("summary", { className: "text-ink-4 text-[11px] font-mono cursor-pointer hover:underline", children: t("principles.readabilityOriginalLabel") }), _jsx("div", { className: "text-ink-4 text-[12px] leading-snug mt-1 font-mono bg-surface/40 px-2 py-1 rounded-[3px] break-all", children: card.originalTitle })] })), _jsx("p", { className: "text-ink-3 text-[13px] line-clamp-3 leading-relaxed", children: card.text }), _jsxs("p", { className: "font-mono text-[11px] text-ink-4 mt-2", children: [t("principles.updatedAt"), ": ", formatDate(card.updatedAt, i18n.language)] }), _jsx(EvolutionBlock, { label: t("principles.blockBasis"), children: t("principles.blockBasisFallback") }), _jsx(EvolutionBlock, { label: t("principles.blockImpact"), children: card.decisionState !== undefined
|
|
313
313
|
? t("principles.blockImpactSeeDecisionView", { defaultValue: "以详情页「当前实际执行」为准(本卡片不据此判断影响)。" })
|
|
314
314
|
: t(BLOCK_IMPACT_KEY[card.status]) }), _jsx(EvolutionBlock, { label: t("principles.blockBehavior"), children: card.action ? card.action : t("principles.blockBehaviorFallback") }), _jsx("div", { className: "mt-3 pt-3 border-t border-line/60", children: _jsxs("div", { className: "flex items-center justify-between gap-3 bg-gov/5 border border-line rounded-[3px] px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] text-ink-4", children: t("principles.govDecision") }), _jsxs("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] border rounded-[2px] px-2 py-0.5 " +
|
|
315
315
|
STATUS_TEXT[card.status] +
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC-PD-CONSOLE-READABILITY-002 切片 1 — 阅读分层(R2/R3/R7)。
|
|
3
|
+
*
|
|
4
|
+
* 长规则正文曾被整段当作加粗标题渲染(Focus 卡标题、原则详情 h1)。这里把
|
|
5
|
+
* 「候选描述/原则正文」拆成短标题预览 + 逐字全文两层:
|
|
6
|
+
* - 短单行文本:只作标题,不产生正文(R7:同一卡片正文只出现一次);
|
|
7
|
+
* - 长文本/多行文本:标题取首个非空行的截断预览(截断只作预览),正文承载
|
|
8
|
+
* 完整原文(逐字保留换行/空行/编号/特殊字符,由容器 whitespace-pre-wrap
|
|
9
|
+
* 渲染)。
|
|
10
|
+
*
|
|
11
|
+
* 这是纯展示拆分:不解析、不改写、不截断存储值——body 永远是原文。
|
|
12
|
+
*/
|
|
13
|
+
export interface ReadingLayout {
|
|
14
|
+
/** 短标题(预览)。空串表示没有可用文本,由调用方落到本地化占位。 */
|
|
15
|
+
title: string;
|
|
16
|
+
/** 逐字全文。null 表示原文即短标题、已由 title 完整承载(不重复渲染)。 */
|
|
17
|
+
body: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function splitReadingTitleBody(text: string | null | undefined): ReadingLayout;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPEC-PD-CONSOLE-READABILITY-002 切片 1 — 阅读分层(R2/R3/R7)。
|
|
3
|
+
*
|
|
4
|
+
* 长规则正文曾被整段当作加粗标题渲染(Focus 卡标题、原则详情 h1)。这里把
|
|
5
|
+
* 「候选描述/原则正文」拆成短标题预览 + 逐字全文两层:
|
|
6
|
+
* - 短单行文本:只作标题,不产生正文(R7:同一卡片正文只出现一次);
|
|
7
|
+
* - 长文本/多行文本:标题取首个非空行的截断预览(截断只作预览),正文承载
|
|
8
|
+
* 完整原文(逐字保留换行/空行/编号/特殊字符,由容器 whitespace-pre-wrap
|
|
9
|
+
* 渲染)。
|
|
10
|
+
*
|
|
11
|
+
* 这是纯展示拆分:不解析、不改写、不截断存储值——body 永远是原文。
|
|
12
|
+
*/
|
|
13
|
+
/** 单行短文本直接作标题的码点上限(约一条短句)。 */
|
|
14
|
+
const SINGLE_LINE_TITLE_MAX = 48;
|
|
15
|
+
/** 长文本标题预览的码点上限(预览不是全文替代品,保持一行可扫读)。 */
|
|
16
|
+
const TITLE_PREVIEW_MAX = 24;
|
|
17
|
+
function firstNonEmptyLine(text) {
|
|
18
|
+
for (const line of text.split("\n")) {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
if (trimmed.length > 0)
|
|
21
|
+
return trimmed;
|
|
22
|
+
}
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
function truncateByCodePoints(text, max) {
|
|
26
|
+
const chars = Array.from(text);
|
|
27
|
+
if (chars.length <= max)
|
|
28
|
+
return text;
|
|
29
|
+
return `${chars.slice(0, max).join("")}…`;
|
|
30
|
+
}
|
|
31
|
+
export function splitReadingTitleBody(text) {
|
|
32
|
+
if (typeof text !== "string")
|
|
33
|
+
return { title: "", body: null };
|
|
34
|
+
const trimmed = text.trim();
|
|
35
|
+
if (trimmed.length === 0)
|
|
36
|
+
return { title: "", body: null };
|
|
37
|
+
const hasNewline = trimmed.includes("\n");
|
|
38
|
+
const codePoints = Array.from(trimmed).length;
|
|
39
|
+
if (!hasNewline && codePoints <= SINGLE_LINE_TITLE_MAX) {
|
|
40
|
+
return { title: trimmed, body: null };
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
title: truncateByCodePoints(firstNonEmptyLine(trimmed), TITLE_PREVIEW_MAX),
|
|
44
|
+
body: text,
|
|
45
|
+
};
|
|
46
|
+
}
|