@principles/pd-cli 1.152.11 → 1.152.13
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/dist/commands/candidate.d.ts +3 -3
- package/dist/commands/candidate.d.ts.map +1 -1
- package/dist/commands/candidate.js +9 -21
- package/dist/commands/candidate.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/dist/services/rulehost-pipeline-runner.js +4 -17
- package/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/package.json +13 -7
- package/dist/commands/__tests__/intent-flag-wiring.test.d.ts +0 -9
- package/dist/commands/__tests__/intent-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/intent-flag-wiring.test.js +0 -237
- package/dist/commands/__tests__/intent-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/legacy-cleanup.test.d.ts +0 -18
- package/dist/commands/__tests__/legacy-cleanup.test.d.ts.map +0 -1
- package/dist/commands/__tests__/legacy-cleanup.test.js +0 -467
- package/dist/commands/__tests__/legacy-cleanup.test.js.map +0 -1
- package/dist/commands/__tests__/mvp-smoke.test.d.ts +0 -15
- package/dist/commands/__tests__/mvp-smoke.test.d.ts.map +0 -1
- package/dist/commands/__tests__/mvp-smoke.test.js +0 -262
- package/dist/commands/__tests__/mvp-smoke.test.js.map +0 -1
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +0 -2
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +0 -1
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +0 -60
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +0 -1
- package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts +0 -21
- package/dist/commands/__tests__/rulecode-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js +0 -186
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/rulecode-handler.test.d.ts +0 -16
- package/dist/commands/__tests__/rulecode-handler.test.d.ts.map +0 -1
- package/dist/commands/__tests__/rulecode-handler.test.js +0 -285
- package/dist/commands/__tests__/rulecode-handler.test.js.map +0 -1
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts +0 -24
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +0 -232
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts +0 -15
- package/dist/commands/__tests__/runtime-activation-nextaction.test.d.ts.map +0 -1
- package/dist/commands/__tests__/runtime-activation-nextaction.test.js +0 -73
- package/dist/commands/__tests__/runtime-activation-nextaction.test.js.map +0 -1
- package/dist/commands/__tests__/runtime-probe-config.test.d.ts +0 -20
- package/dist/commands/__tests__/runtime-probe-config.test.d.ts.map +0 -1
- package/dist/commands/__tests__/runtime-probe-config.test.js +0 -389
- package/dist/commands/__tests__/runtime-probe-config.test.js.map +0 -1
- package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts +0 -9
- package/dist/commands/__tests__/telemetry-flag-wiring.test.d.ts.map +0 -1
- package/dist/commands/__tests__/telemetry-flag-wiring.test.js +0 -76
- package/dist/commands/__tests__/telemetry-flag-wiring.test.js.map +0 -1
- package/dist/commands/__tests__/version.test.d.ts +0 -2
- package/dist/commands/__tests__/version.test.d.ts.map +0 -1
- package/dist/commands/__tests__/version.test.js +0 -238
- package/dist/commands/__tests__/version.test.js.map +0 -1
- package/dist/commands/runtime-uat.guard.test.d.ts +0 -2
- package/dist/commands/runtime-uat.guard.test.d.ts.map +0 -1
- package/dist/commands/runtime-uat.guard.test.js +0 -153
- package/dist/commands/runtime-uat.guard.test.js.map +0 -1
- package/dist/services/__tests__/cli-output.test.d.ts +0 -18
- package/dist/services/__tests__/cli-output.test.d.ts.map +0 -1
- package/dist/services/__tests__/cli-output.test.js +0 -103
- package/dist/services/__tests__/cli-output.test.js.map +0 -1
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +0 -2
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +0 -1
- package/dist/services/__tests__/evaluator-runner-deps.test.js +0 -343
- package/dist/services/__tests__/evaluator-runner-deps.test.js.map +0 -1
- package/dist/services/__tests__/rulehost-readiness.test.d.ts +0 -2
- package/dist/services/__tests__/rulehost-readiness.test.d.ts.map +0 -1
- package/dist/services/__tests__/rulehost-readiness.test.js +0 -311
- package/dist/services/__tests__/rulehost-readiness.test.js.map +0 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts +0 -18
- package/dist/services/__tests__/runtime-adapter-resolver.test.d.ts.map +0 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.js +0 -673
- package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +0 -1
- package/dist/utils/production-workspace-guard.test.d.ts +0 -2
- package/dist/utils/production-workspace-guard.test.d.ts.map +0 -1
- package/dist/utils/production-workspace-guard.test.js +0 -137
- package/dist/utils/production-workspace-guard.test.js.map +0 -1
- package/scripts/llm-dogfood.ts +0 -456
- package/scripts/migrate-illegal-expected-decision.ts +0 -252
- package/src/commands/__tests__/intent-flag-wiring.test.ts +0 -282
- package/src/commands/__tests__/legacy-cleanup.test.ts +0 -602
- package/src/commands/__tests__/mvp-smoke.test.ts +0 -300
- package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +0 -73
- package/src/commands/__tests__/rulecode-flag-wiring.test.ts +0 -237
- package/src/commands/__tests__/rulecode-handler.test.ts +0 -369
- package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +0 -292
- package/src/commands/__tests__/runtime-activation-nextaction.test.ts +0 -81
- package/src/commands/__tests__/runtime-probe-config.test.ts +0 -432
- package/src/commands/__tests__/telemetry-flag-wiring.test.ts +0 -84
- package/src/commands/__tests__/version.test.ts +0 -251
- package/src/commands/admission-gate.ts +0 -30
- package/src/commands/artifact.ts +0 -82
- package/src/commands/build-trajectory-evidence.ts +0 -298
- package/src/commands/candidate.ts +0 -1260
- package/src/commands/codex-ingest-catchup.ts +0 -114
- package/src/commands/codex-ingest-quarantine.ts +0 -127
- package/src/commands/codex-reconcile.ts +0 -84
- package/src/commands/codex-setup.ts +0 -476
- package/src/commands/codex-worker.ts +0 -185
- package/src/commands/command-helpers.ts +0 -24
- package/src/commands/config-doctor.ts +0 -193
- package/src/commands/console.ts +0 -797
- package/src/commands/context.ts +0 -72
- package/src/commands/demo-story-a.ts +0 -241
- package/src/commands/diagnose.ts +0 -756
- package/src/commands/errors-list.ts +0 -441
- package/src/commands/evolution-tasks-list.ts +0 -48
- package/src/commands/evolution-tasks-show.ts +0 -62
- package/src/commands/flow.ts +0 -60
- package/src/commands/health-codex.ts +0 -576
- package/src/commands/health.ts +0 -315
- package/src/commands/history.ts +0 -63
- package/src/commands/intent.ts +0 -457
- package/src/commands/legacy-cleanup.ts +0 -531
- package/src/commands/legacy-import.ts +0 -104
- package/src/commands/mvp-smoke.ts +0 -160
- package/src/commands/pain-evidence.ts +0 -226
- package/src/commands/pain-list.ts +0 -239
- package/src/commands/pain-record.ts +0 -612
- package/src/commands/pain-retry.ts +0 -916
- package/src/commands/principles-stats.ts +0 -700
- package/src/commands/proven-channel-baseline.ts +0 -115
- package/src/commands/quality-scorecard.ts +0 -272
- package/src/commands/remediation-output.ts +0 -66
- package/src/commands/rulecode.ts +0 -465
- package/src/commands/run.ts +0 -89
- package/src/commands/runtime-activation.ts +0 -1629
- package/src/commands/runtime-artifact-repair.ts +0 -524
- package/src/commands/runtime-canary.ts +0 -395
- package/src/commands/runtime-compatibility-scan.ts +0 -105
- package/src/commands/runtime-diagnostics-export.ts +0 -229
- package/src/commands/runtime-features.ts +0 -153
- package/src/commands/runtime-gfi-snapshot.ts +0 -135
- package/src/commands/runtime-health-snapshot.ts +0 -108
- package/src/commands/runtime-init.ts +0 -429
- package/src/commands/runtime-internalization-enqueue-successors.ts +0 -530
- package/src/commands/runtime-internalization-integrity-repair.ts +0 -79
- package/src/commands/runtime-internalization-integrity.ts +0 -107
- package/src/commands/runtime-internalization-queue.ts +0 -135
- package/src/commands/runtime-internalization-retry.ts +0 -191
- package/src/commands/runtime-internalization-run-once.ts +0 -791
- package/src/commands/runtime-internalization-run-rulehost.ts +0 -749
- package/src/commands/runtime-internalization-wake-once.ts +0 -87
- package/src/commands/runtime-pain-flood-simulation.ts +0 -121
- package/src/commands/runtime-pruning.ts +0 -438
- package/src/commands/runtime-recovery-failed-tasks.ts +0 -258
- package/src/commands/runtime-recovery.ts +0 -109
- package/src/commands/runtime-synthetic-baseline.ts +0 -70
- package/src/commands/runtime-uat.guard.test.ts +0 -175
- package/src/commands/runtime-uat.ts +0 -380
- package/src/commands/runtime.ts +0 -473
- package/src/commands/samples-list.ts +0 -39
- package/src/commands/samples-review.ts +0 -36
- package/src/commands/task.ts +0 -271
- package/src/commands/telemetry.ts +0 -349
- package/src/commands/trace.ts +0 -174
- package/src/commands/trajectory-db-unavailable.ts +0 -19
- package/src/commands/trajectory.ts +0 -64
- package/src/commands/version.ts +0 -49
- package/src/config-reader.ts +0 -122
- package/src/index.ts +0 -1276
- package/src/legacy/legacy-import.ts +0 -179
- package/src/legacy/session-history-import.ts +0 -203
- package/src/resolve-workspace.ts +0 -106
- package/src/services/__tests__/cli-output.test.ts +0 -130
- package/src/services/__tests__/evaluator-runner-deps.test.ts +0 -382
- package/src/services/__tests__/rulehost-readiness.test.ts +0 -363
- package/src/services/__tests__/runtime-adapter-resolver.test.ts +0 -793
- package/src/services/cli-output.ts +0 -95
- package/src/services/config-doctor.ts +0 -697
- package/src/services/console-launcher.ts +0 -537
- package/src/services/demo-rule-compiler.ts +0 -40
- package/src/services/demo-story-a-runner.ts +0 -471
- package/src/services/mainline-snapshot-assembler.ts +0 -579
- package/src/services/pain-flood-simulation-runner.ts +0 -462
- package/src/services/pd-config-loader.ts +0 -433
- package/src/services/proven-channel-baseline-runner.ts +0 -157
- package/src/services/quality-scorecard/data-extractor.ts +0 -150
- package/src/services/quality-scorecard/local-evaluator.ts +0 -142
- package/src/services/quality-scorecard/strong-model-gate.ts +0 -202
- package/src/services/resolve-runtime-from-pd-config.ts +0 -230
- package/src/services/rulehost-pipeline-runner.ts +0 -1216
- package/src/services/rulehost-readiness.ts +0 -326
- package/src/services/runtime-adapter-resolver.ts +0 -364
- package/src/services/synthetic-baseline-runner.ts +0 -400
- package/src/services/version-report.ts +0 -253
- package/src/services/workspace-tool-semantics.ts +0 -26
- package/src/utils/path-security.ts +0 -96
- package/src/utils/production-workspace-guard.test.ts +0 -157
- package/src/utils/production-workspace-guard.ts +0 -210
- package/tests/bdd/cli-contract.steps.ts +0 -346
- package/tests/bdd/principles-stats.steps.ts +0 -211
- package/tests/bdd/support/gherkin-loader.ts +0 -147
- package/tests/bdd/support/repo-root.ts +0 -62
- package/tests/bdd/support/vitest-bdd.ts +0 -179
- package/tests/commands/admission-gate-coverage.test.ts +0 -79
- package/tests/commands/build-trajectory-evidence.test.ts +0 -508
- package/tests/commands/candidate-audit-repair.test.ts +0 -421
- package/tests/commands/candidate-intake.test.ts +0 -732
- package/tests/commands/candidate-internalization-backfill.test.ts +0 -548
- package/tests/commands/candidate-internalize-lineage.test.ts +0 -565
- package/tests/commands/candidate-internalize.test.ts +0 -299
- package/tests/commands/candidate-route.test.ts +0 -329
- package/tests/commands/candidate-show.test.ts +0 -95
- package/tests/commands/cli-command-tree.test.ts +0 -144
- package/tests/commands/cli-help-snapshot.test.ts +0 -155
- package/tests/commands/cli-skill-contract.test.ts +0 -121
- package/tests/commands/codex-consent-reversibility.steps.test.ts +0 -299
- package/tests/commands/codex-ingest-quarantine.test.ts +0 -136
- package/tests/commands/codex-setup.test.ts +0 -297
- package/tests/commands/codex-worker-daemon.test.ts +0 -95
- package/tests/commands/codex-worker-registration.test.ts +0 -207
- package/tests/commands/config-doctor.test.ts +0 -336
- package/tests/commands/console-launcher-edge-cases.test.ts +0 -388
- package/tests/commands/console-open.test.ts +0 -1381
- package/tests/commands/context.test.ts +0 -114
- package/tests/commands/demo-story-a.test.ts +0 -318
- package/tests/commands/diagnose.test.ts +0 -1803
- package/tests/commands/errors-list.test.ts +0 -679
- package/tests/commands/evolution-tasks-list.test.ts +0 -41
- package/tests/commands/evolution-tasks-show.test.ts +0 -46
- package/tests/commands/health-codex.test.ts +0 -349
- package/tests/commands/health-host-wiring.test.ts +0 -76
- package/tests/commands/health-reliability.test.ts +0 -99
- package/tests/commands/health.test.ts +0 -413
- package/tests/commands/intent.test.ts +0 -321
- package/tests/commands/legacy-cleanup.test.ts +0 -148
- package/tests/commands/pain-evidence.test.ts +0 -518
- package/tests/commands/pain-list.test.ts +0 -219
- package/tests/commands/pain-record-async.test.ts +0 -335
- package/tests/commands/pain-record-session-parser.test.ts +0 -228
- package/tests/commands/pain-record.test.ts +0 -851
- package/tests/commands/pain-retry.test.ts +0 -1121
- package/tests/commands/plugin-config-resolution-cutover.test.ts +0 -229
- package/tests/commands/pri-393-runtime-config-unification.test.ts +0 -443
- package/tests/commands/principles-stats-wiring.test.ts +0 -135
- package/tests/commands/principles-stats.test.ts +0 -274
- package/tests/commands/product-path-regression.test.ts +0 -86
- package/tests/commands/proven-channel-baseline.test.ts +0 -441
- package/tests/commands/rulecode.test.ts +0 -177
- package/tests/commands/run-rulehost-handler.test.ts +0 -908
- package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +0 -227
- package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +0 -167
- package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +0 -262
- package/tests/commands/runtime-activation-governance-audit.test.ts +0 -95
- package/tests/commands/runtime-activation-list-flag-wiring.test.ts +0 -193
- package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +0 -248
- package/tests/commands/runtime-activation-shadow-telemetry.test.ts +0 -130
- package/tests/commands/runtime-activation.test.ts +0 -2052
- package/tests/commands/runtime-artifact-repair-registration.test.ts +0 -74
- package/tests/commands/runtime-artifact-repair.test.ts +0 -404
- package/tests/commands/runtime-canary.test.ts +0 -430
- package/tests/commands/runtime-compatibility-scan.test.ts +0 -146
- package/tests/commands/runtime-diagnostics-export.test.ts +0 -175
- package/tests/commands/runtime-features.test.ts +0 -249
- package/tests/commands/runtime-health-snapshot.test.ts +0 -358
- package/tests/commands/runtime-init-empty-workspace.test.ts +0 -252
- package/tests/commands/runtime-init.test.ts +0 -420
- package/tests/commands/runtime-internalization-enqueue-successors.test.ts +0 -782
- package/tests/commands/runtime-internalization-integrity-repair.test.ts +0 -239
- package/tests/commands/runtime-internalization-integrity.test.ts +0 -140
- package/tests/commands/runtime-internalization-queue.test.ts +0 -380
- package/tests/commands/runtime-internalization-retry-owner-authority.test.ts +0 -431
- package/tests/commands/runtime-internalization-run-once-evaluator-parity.test.ts +0 -269
- package/tests/commands/runtime-internalization-run-once-language.test.ts +0 -303
- package/tests/commands/runtime-internalization-run-once-real-config.test.ts +0 -237
- package/tests/commands/runtime-internalization-run-once-rollout-parity.test.ts +0 -706
- package/tests/commands/runtime-internalization-run-once.test.ts +0 -1791
- package/tests/commands/runtime-internalization-wake-once.test.ts +0 -171
- package/tests/commands/runtime-internalization.test.ts +0 -53
- package/tests/commands/runtime-pain-flood-simulation.test.ts +0 -418
- package/tests/commands/runtime-pruning.test.ts +0 -694
- package/tests/commands/runtime-recovery-failed-tasks.test.ts +0 -328
- package/tests/commands/runtime-recovery.test.ts +0 -120
- package/tests/commands/runtime-synthetic-baseline.test.ts +0 -249
- package/tests/commands/runtime-uat.test.ts +0 -397
- package/tests/commands/runtime.test.ts +0 -392
- package/tests/commands/samples-list.test.ts +0 -43
- package/tests/commands/samples-review.test.ts +0 -41
- package/tests/commands/task.test.ts +0 -153
- package/tests/commands/telemetry.test.ts +0 -190
- package/tests/commands/trace.test.ts +0 -316
- package/tests/config-reader.test.ts +0 -143
- package/tests/e2e/candidate-intake-e2e.test.ts +0 -338
- package/tests/e2e/cli-full-flow.test.ts +0 -233
- package/tests/e2e/cross-package-acceptance.test.ts +0 -635
- package/tests/helpers/pd-cli-path.ts +0 -5
- package/tests/pd-cli-smoke.test.ts +0 -13
- package/tests/resolve-workspace.test.ts +0 -196
- package/tests/services/config-doctor-signal-health.test.ts +0 -109
- package/tests/services/console-launcher.test.ts +0 -114
- package/tests/services/demo-rule-compiler.test.ts +0 -242
- package/tests/services/mainline-snapshot-assembler.test.ts +0 -572
- package/tests/services/pd-config-loader.test.ts +0 -505
- package/tests/services/proven-channel-baseline-runner.test.ts +0 -30
- package/tests/services/quality-scorecard/strong-model-gate.test.ts +0 -133
- package/tests/services/resolve-runtime-from-pd-config.test.ts +0 -485
- package/tests/services/rulehost-pipeline-e2e.test.ts +0 -538
- package/tests/services/rulehost-pipeline-runner.test.ts +0 -1175
- package/tests/services/workspace-tool-semantics.test.ts +0 -76
- package/tests/utils/path-security.test.ts +0 -180
- package/tsconfig.json +0 -26
- package/vitest.config.ts +0 -9
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pd pain list command unit tests — PRI-640 (Host Attribution v0.1).
|
|
3
|
-
*
|
|
4
|
-
* External contract: listPains reads trajectory.db readonly and filters by
|
|
5
|
-
* host (openclaw / codex / unknown); handlePainList preserves the strict
|
|
6
|
-
* --json contract and degraded-failure shapes (cli-1/cli-6).
|
|
7
|
-
*/
|
|
8
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
9
|
-
import * as fs from 'fs';
|
|
10
|
-
import * as os from 'os';
|
|
11
|
-
import * as path from 'path';
|
|
12
|
-
import Database from 'better-sqlite3';
|
|
13
|
-
|
|
14
|
-
vi.mock('../../src/resolve-workspace.js', () => ({
|
|
15
|
-
resolveWorkspaceDir: vi.fn().mockReturnValue('/fake/workspace'),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
import { handlePainList, listPains } from '../../src/commands/pain-list.js';
|
|
19
|
-
import { resolveWorkspaceDir } from '../../src/resolve-workspace.js';
|
|
20
|
-
|
|
21
|
-
const tempDirs: string[] = [];
|
|
22
|
-
|
|
23
|
-
function openWorkspaceDb(prefix: string): { workspaceDir: string; db: Database.Database } {
|
|
24
|
-
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
25
|
-
tempDirs.push(workspaceDir);
|
|
26
|
-
const stateDir = path.join(workspaceDir, '.state');
|
|
27
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
28
|
-
return { workspaceDir, db: new Database(path.join(stateDir, 'trajectory.db')) };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Create a temp workspace whose trajectory.db uses the PRI-640 schema (host_kind column). */
|
|
32
|
-
function makeWorkspace(): { workspaceDir: string; db: Database.Database } {
|
|
33
|
-
const { workspaceDir, db } = openWorkspaceDb('pd-pain-list-');
|
|
34
|
-
db.prepare('CREATE TABLE sessions (session_id TEXT PRIMARY KEY, started_at TEXT NOT NULL, updated_at TEXT NOT NULL)').run();
|
|
35
|
-
db.prepare(`CREATE TABLE pain_events (
|
|
36
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37
|
-
session_id TEXT NOT NULL, source TEXT NOT NULL, score REAL NOT NULL, reason TEXT,
|
|
38
|
-
severity TEXT, origin TEXT, confidence REAL, text TEXT,
|
|
39
|
-
canonical_pain_id TEXT, runtime_task_id TEXT, host_kind TEXT, created_at TEXT NOT NULL
|
|
40
|
-
)`).run();
|
|
41
|
-
db.prepare('CREATE UNIQUE INDEX idx_pain_events_canonical_pain_id ON pain_events(canonical_pain_id) WHERE canonical_pain_id IS NOT NULL').run();
|
|
42
|
-
return { workspaceDir, db };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Create a temp workspace whose trajectory.db predates PRI-640 (no host_kind column). */
|
|
46
|
-
function makePre640Workspace(): { workspaceDir: string; db: Database.Database } {
|
|
47
|
-
const { workspaceDir, db } = openWorkspaceDb('pd-pain-pre640-');
|
|
48
|
-
db.prepare('CREATE TABLE sessions (session_id TEXT PRIMARY KEY, started_at TEXT NOT NULL, updated_at TEXT NOT NULL)').run();
|
|
49
|
-
db.prepare(`CREATE TABLE pain_events (
|
|
50
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
51
|
-
session_id TEXT NOT NULL, source TEXT NOT NULL, score REAL NOT NULL, reason TEXT,
|
|
52
|
-
severity TEXT, origin TEXT, confidence REAL, text TEXT,
|
|
53
|
-
canonical_pain_id TEXT, runtime_task_id TEXT, created_at TEXT NOT NULL
|
|
54
|
-
)`).run();
|
|
55
|
-
db.prepare('CREATE UNIQUE INDEX idx_pain_events_canonical_pain_id ON pain_events(canonical_pain_id) WHERE canonical_pain_id IS NOT NULL').run();
|
|
56
|
-
return { workspaceDir, db };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function seedPain(db: Database.Database, row: { id: number; source: string; canonical: string; host: string | null; task?: string | null; created: string }): void {
|
|
60
|
-
db.prepare(`INSERT INTO pain_events (id, session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, host_kind, created_at)
|
|
61
|
-
VALUES (?, 's1', ?, 80, 'r', 'moderate', 'system_infer', NULL, NULL, ?, ?, ?, ?)`)
|
|
62
|
-
.run(row.id, row.source, row.canonical, row.task ?? null, row.host, row.created);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function dbPathOf(db: Database.Database): string {
|
|
66
|
-
return db.name;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
afterEach(() => {
|
|
70
|
-
for (const dir of tempDirs.splice(0)) {
|
|
71
|
-
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 20 });
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('listPains (PRI-640 host filter)', () => {
|
|
76
|
-
it('filters host=openclaw / codex / unknown and reports NULL as unknown', async () => {
|
|
77
|
-
const { workspaceDir, db } = makeWorkspace();
|
|
78
|
-
seedPain(db, { id: 1, source: 'user_correction', canonical: 'pain_oc_1', host: 'openclaw', task: 'diagnosis_pain_oc_1', created: '2026-09-01T10:00:00.000Z' });
|
|
79
|
-
seedPain(db, { id: 2, source: 'tool_failure', canonical: 'pain_cx_1', host: 'codex', created: '2026-09-01T11:00:00.000Z' });
|
|
80
|
-
seedPain(db, { id: 3, source: 'manual', canonical: 'pain_unknown_1', host: null, created: '2026-09-01T12:00:00.000Z' });
|
|
81
|
-
const dbPath = dbPathOf(db);
|
|
82
|
-
db.close();
|
|
83
|
-
|
|
84
|
-
const all = await listPains(dbPath, { limit: 10 });
|
|
85
|
-
expect(all.count).toBe(3);
|
|
86
|
-
expect(all.pains.map((p) => p.host)).toEqual(['unknown', 'codex', 'openclaw']); // newest first
|
|
87
|
-
expect(all.pains.find((p) => p.painId === 'pain_oc_1')).toMatchObject({ host: 'openclaw', source: 'user_correction', runtimeTaskId: 'diagnosis_pain_oc_1', createdAt: '2026-09-01T10:00:00.000Z' });
|
|
88
|
-
expect(all.workspace).toBe(workspaceDir);
|
|
89
|
-
|
|
90
|
-
expect((await listPains(dbPath, { limit: 10, host: 'openclaw' })).pains.map((p) => p.painId)).toEqual(['pain_oc_1']);
|
|
91
|
-
expect((await listPains(dbPath, { limit: 10, host: 'codex' })).pains.map((p) => p.painId)).toEqual(['pain_cx_1']);
|
|
92
|
-
expect((await listPains(dbPath, { limit: 10, host: 'unknown' })).pains.map((p) => p.painId)).toEqual(['pain_unknown_1']);
|
|
93
|
-
expect(all.warnings).toEqual([]);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('reports legacy rows without a canonical id as row:<id>', async () => {
|
|
97
|
-
const { db } = makeWorkspace();
|
|
98
|
-
db.prepare(`INSERT INTO pain_events (session_id, source, score, created_at) VALUES ('s1', 'correction_rejected', 60, '2026-08-01T00:00:00.000Z')`).run();
|
|
99
|
-
const dbPath = dbPathOf(db);
|
|
100
|
-
db.close();
|
|
101
|
-
|
|
102
|
-
const result = await listPains(dbPath, { limit: 10 });
|
|
103
|
-
expect(result.count).toBe(1);
|
|
104
|
-
expect(result.pains[0]?.painId).toBe('row:1');
|
|
105
|
-
expect(result.pains[0]?.host).toBe('unknown');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('PRI-743: byHost reports the full-table distribution independent of --host/--limit', async () => {
|
|
109
|
-
const { db } = makeWorkspace();
|
|
110
|
-
seedPain(db, { id: 1, source: 'user_correction', canonical: 'pain_oc_1', host: 'openclaw', created: '2026-09-01T10:00:00.000Z' });
|
|
111
|
-
seedPain(db, { id: 2, source: 'user_correction', canonical: 'pain_oc_2', host: 'openclaw', created: '2026-09-01T10:05:00.000Z' });
|
|
112
|
-
seedPain(db, { id: 3, source: 'tool_failure', canonical: 'pain_cx_1', host: 'codex', created: '2026-09-01T11:00:00.000Z' });
|
|
113
|
-
seedPain(db, { id: 4, source: 'manual', canonical: 'pain_null_1', host: null, created: '2026-09-01T12:00:00.000Z' });
|
|
114
|
-
seedPain(db, { id: 5, source: 'manual', canonical: 'pain_null_2', host: null, created: '2026-09-01T12:05:00.000Z' });
|
|
115
|
-
const dbPath = dbPathOf(db);
|
|
116
|
-
db.close();
|
|
117
|
-
|
|
118
|
-
// --limit narrows the row list but must NOT narrow the distribution.
|
|
119
|
-
const limited = await listPains(dbPath, { limit: 2 });
|
|
120
|
-
expect(limited.count).toBe(2);
|
|
121
|
-
expect(limited.byHost).toEqual({ openclaw: 2, codex: 1, unknown: 2 });
|
|
122
|
-
|
|
123
|
-
// --host narrows the row list but must NOT narrow the distribution.
|
|
124
|
-
const filtered = await listPains(dbPath, { limit: 10, host: 'codex' });
|
|
125
|
-
expect(filtered.count).toBe(1);
|
|
126
|
-
expect(filtered.byHost).toEqual({ openclaw: 2, codex: 1, unknown: 2 });
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('PRI-743: byHost is null (never guessed) on a pre-PRI-640 database', async () => {
|
|
130
|
-
const { db } = makePre640Workspace();
|
|
131
|
-
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at)
|
|
132
|
-
VALUES ('s1', 'tool_failure', 70, 'r', 'moderate', 'system_infer', NULL, NULL, 'pain_legacy', NULL, '2026-08-01T00:00:00.000Z')`).run();
|
|
133
|
-
const dbPath = dbPathOf(db);
|
|
134
|
-
db.close();
|
|
135
|
-
|
|
136
|
-
const result = await listPains(dbPath, { limit: 10 });
|
|
137
|
-
expect(result.byHost).toBeNull();
|
|
138
|
-
expect(result.warnings).toContain('host_kind_column_missing');
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('degrades observably on a pre-PRI-640 database without the host_kind column (rc-9)', async () => {
|
|
142
|
-
const { db } = makePre640Workspace();
|
|
143
|
-
db.prepare(`INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, created_at)
|
|
144
|
-
VALUES ('s1', 'tool_failure', 70, 'r', 'moderate', 'system_infer', NULL, NULL, 'pain_legacy', NULL, '2026-08-01T00:00:00.000Z')`).run();
|
|
145
|
-
const dbPath = dbPathOf(db);
|
|
146
|
-
db.close();
|
|
147
|
-
|
|
148
|
-
const all = await listPains(dbPath, { limit: 10 });
|
|
149
|
-
expect(all.count).toBe(1);
|
|
150
|
-
expect(all.pains[0]?.host).toBe('unknown');
|
|
151
|
-
expect(all.warnings).toContain('host_kind_column_missing');
|
|
152
|
-
|
|
153
|
-
// Host filters that cannot match degrade to an explicit empty result.
|
|
154
|
-
expect(await listPains(dbPath, { limit: 10, host: 'codex' })).toMatchObject({ count: 0, pains: [] });
|
|
155
|
-
// unknown still returns the legacy rows.
|
|
156
|
-
expect((await listPains(dbPath, { limit: 10, host: 'unknown' })).count).toBe(1);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('handlePainList (CLI contract)', () => {
|
|
161
|
-
let exitSpy: ReturnType<typeof vi.spyOn>;
|
|
162
|
-
let logSpy: ReturnType<typeof vi.spyOn>;
|
|
163
|
-
let errSpy: ReturnType<typeof vi.spyOn>;
|
|
164
|
-
let workspaceDir: string;
|
|
165
|
-
|
|
166
|
-
beforeEach(() => {
|
|
167
|
-
exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => undefined) as () => never);
|
|
168
|
-
logSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
169
|
-
errSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
170
|
-
const made = makeWorkspace();
|
|
171
|
-
workspaceDir = made.workspaceDir;
|
|
172
|
-
seedPain(made.db, { id: 1, source: 'user_correction', canonical: 'pain_cli_oc', host: 'openclaw', task: 't1', created: '2026-09-01T09:00:00.000Z' });
|
|
173
|
-
seedPain(made.db, { id: 2, source: 'tool_failure', canonical: 'pain_cli_cx', host: 'codex', created: '2026-09-01T09:30:00.000Z' });
|
|
174
|
-
made.db.close();
|
|
175
|
-
vi.mocked(resolveWorkspaceDir).mockReturnValue(workspaceDir);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
afterEach(() => {
|
|
179
|
-
exitSpy.mockRestore();
|
|
180
|
-
logSpy.mockRestore();
|
|
181
|
-
errSpy.mockRestore();
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('--json emits one valid JSON object with stable host values', async () => {
|
|
185
|
-
await handlePainList({ json: true });
|
|
186
|
-
expect(exitSpy).not.toHaveBeenCalled();
|
|
187
|
-
const raw = logSpy.mock.calls.map((args) => String(args[0])).join('\n');
|
|
188
|
-
const parsed = JSON.parse(raw) as { count: number; pains: { host: string; painId: string }[]; hostFilter: unknown; warnings: string[]; byHost: { openclaw: number; codex: number; unknown: number } | null };
|
|
189
|
-
expect(parsed.count).toBe(2);
|
|
190
|
-
expect(parsed.pains.map((p) => `${p.painId}:${p.host}`).sort()).toEqual(['pain_cli_cx:codex', 'pain_cli_oc:openclaw']);
|
|
191
|
-
expect(parsed.hostFilter).toBeNull();
|
|
192
|
-
expect(parsed.warnings).toEqual([]);
|
|
193
|
-
expect(parsed.byHost).toEqual({ openclaw: 1, codex: 1, unknown: 0 });
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('--host filter is reflected in the JSON result', async () => {
|
|
197
|
-
await handlePainList({ json: true, host: 'codex' });
|
|
198
|
-
const parsed = JSON.parse(logSpy.mock.calls.map((args) => String(args[0])).join('\n')) as { count: number; hostFilter: string; pains: unknown[] };
|
|
199
|
-
expect(parsed).toMatchObject({ count: 1, hostFilter: 'codex' });
|
|
200
|
-
expect(parsed.pains).toHaveLength(1);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('an invalid --host value fails loudly with a structured reason (cli-6)', async () => {
|
|
204
|
-
await handlePainList({ json: true, host: 'claude' });
|
|
205
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
206
|
-
const parsed = JSON.parse(logSpy.mock.calls.map((args) => String(args[0])).join('\n')) as { status: string; reason: string; nextAction: string };
|
|
207
|
-
expect(parsed).toMatchObject({ status: 'failed', reason: 'invalid_host_filter' });
|
|
208
|
-
expect(parsed.nextAction).toContain('--host openclaw');
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('a missing trajectory.db degrades with reason and next action (cli-6)', async () => {
|
|
212
|
-
fs.rmSync(path.join(workspaceDir, '.state', 'trajectory.db'));
|
|
213
|
-
await handlePainList({ json: true });
|
|
214
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
215
|
-
const parsed = JSON.parse(logSpy.mock.calls.map((args) => String(args[0])).join('\n')) as { status: string; reason: string; nextAction: string };
|
|
216
|
-
expect(parsed).toMatchObject({ status: 'failed', reason: 'trajectory_db_not_found' });
|
|
217
|
-
expect(parsed.nextAction).toContain('pd runtime init');
|
|
218
|
-
});
|
|
219
|
-
});
|
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pd pain record async mode tests — PRI-369
|
|
3
|
-
*
|
|
4
|
-
* Tests the async CLI behavior:
|
|
5
|
-
* - --json output is a single parseable JSON object in async mode
|
|
6
|
-
* - Failed task-creation spawns nothing and mutates nothing
|
|
7
|
-
* - Flag off => legacy sync behavior unchanged
|
|
8
|
-
* - --wait overrides async flag to force sync
|
|
9
|
-
* - asyncMode=true when flag enabled and --wait not set
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
12
|
-
|
|
13
|
-
// ── Mock setup ──────────────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
let mockRecordPainResult: PainToPrincipleOutput;
|
|
16
|
-
let lastServiceOpts: Record<string, unknown> | null = null;
|
|
17
|
-
let mockIsFeatureEnabledReturn = false;
|
|
18
|
-
|
|
19
|
-
vi.mock('../../src/resolve-workspace.js', () => ({
|
|
20
|
-
resolveWorkspaceDir: vi.fn().mockReturnValue('/tmp/fake-workspace'),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
vi.mock('fs', () => ({
|
|
24
|
-
existsSync: vi.fn().mockReturnValue(false),
|
|
25
|
-
readFileSync: vi.fn(),
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
vi.mock('../../src/commands/build-trajectory-evidence.js', () => ({
|
|
29
|
-
buildTrajectoryEvidenceFromDb: vi.fn().mockReturnValue([
|
|
30
|
-
{ sourceRef: 'owner_reported:cli', note: 'No session context available' },
|
|
31
|
-
]),
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
vi.mock('@principles/core/runtime-v2', async (importOriginal) => {
|
|
35
|
-
// PRI-642 review blocker 1: keep the REAL core evaluatePainIngress (the
|
|
36
|
-
// shared semantic authority) — only service/IO classes are mocked.
|
|
37
|
-
const actual = await importOriginal<typeof import('@principles/core/runtime-v2')>();
|
|
38
|
-
return {
|
|
39
|
-
...actual,
|
|
40
|
-
PainToPrincipleService: vi.fn().mockImplementation(function(this: Record<string, unknown>, opts: Record<string, unknown>) {
|
|
41
|
-
lastServiceOpts = opts;
|
|
42
|
-
return {
|
|
43
|
-
recordPain: vi.fn(async () => mockRecordPainResult),
|
|
44
|
-
};
|
|
45
|
-
}),
|
|
46
|
-
PrincipleTreeLedgerAdapter: vi.fn().mockImplementation(function() { return {}; }),
|
|
47
|
-
computeEffectivePdConfig: vi.fn().mockReturnValue({
|
|
48
|
-
runtimeKind: 'pi-ai',
|
|
49
|
-
provider: 'test-provider',
|
|
50
|
-
model: 'test-model',
|
|
51
|
-
apiKeyEnv: 'TEST_KEY',
|
|
52
|
-
timeoutMs: 300000,
|
|
53
|
-
agentId: 'main',
|
|
54
|
-
language: 'zh-CN',
|
|
55
|
-
warnings: [],
|
|
56
|
-
}),
|
|
57
|
-
resolveRuntimeConfig: vi.fn().mockReturnValue({
|
|
58
|
-
runtimeKind: 'pi-ai',
|
|
59
|
-
provider: 'test-provider',
|
|
60
|
-
model: 'test-model',
|
|
61
|
-
apiKeyEnv: 'TEST_KEY',
|
|
62
|
-
timeoutMs: 300000,
|
|
63
|
-
agentId: 'main',
|
|
64
|
-
}),
|
|
65
|
-
isRuntimeConfigError: vi.fn().mockReturnValue(false),
|
|
66
|
-
resolveOutputLanguage: vi.fn().mockReturnValue({ outputLanguage: 'zh-CN' }),
|
|
67
|
-
isFeatureEnabled: vi.fn().mockImplementation(() => mockIsFeatureEnabledReturn),
|
|
68
|
-
};
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
vi.mock('../../src/services/pd-config-loader.js', () => ({
|
|
72
|
-
loadPdConfig: vi.fn().mockReturnValue({
|
|
73
|
-
ok: true,
|
|
74
|
-
effective: { config: {}, source: 'defaults', warnings: [] },
|
|
75
|
-
source: 'defaults',
|
|
76
|
-
configPath: '/tmp/fake-workspace/.pd/config.yaml',
|
|
77
|
-
warnings: [],
|
|
78
|
-
legacyFilesDetected: [],
|
|
79
|
-
}),
|
|
80
|
-
computeFlagsFromLoadResult: vi.fn().mockReturnValue({
|
|
81
|
-
flags: {},
|
|
82
|
-
enabledChannels: [],
|
|
83
|
-
warnings: [],
|
|
84
|
-
}),
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
|
-
import { handlePainRecord } from '../../src/commands/pain-record.js';
|
|
88
|
-
import type { PainToPrincipleOutput, FailureCategory } from '@principles/core/runtime-v2';
|
|
89
|
-
|
|
90
|
-
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
91
|
-
|
|
92
|
-
function mockProcessExit() {
|
|
93
|
-
return vi.spyOn(process, 'exit').mockImplementation((() => undefined) as () => never);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const SUBMITTED_RESULT: PainToPrincipleOutput = {
|
|
97
|
-
status: 'submitted',
|
|
98
|
-
painId: 'manual_123_abc',
|
|
99
|
-
taskId: 'diagnosis_manual_123_abc',
|
|
100
|
-
candidateIds: [],
|
|
101
|
-
ledgerEntryIds: [],
|
|
102
|
-
observabilityWarnings: [],
|
|
103
|
-
latencyMs: 120,
|
|
104
|
-
message: "Diagnosis submitted. Use 'pd task show diagnosis_manual_123_abc' to check progress.",
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const SUCCEEDED_RESULT: PainToPrincipleOutput = {
|
|
108
|
-
status: 'succeeded',
|
|
109
|
-
painId: 'manual_123_abc',
|
|
110
|
-
taskId: 'diagnosis_manual_123_abc',
|
|
111
|
-
runId: 'run-001',
|
|
112
|
-
artifactId: 'art-001',
|
|
113
|
-
candidateIds: ['c1'],
|
|
114
|
-
ledgerEntryIds: ['l1'],
|
|
115
|
-
observabilityWarnings: [],
|
|
116
|
-
latencyMs: 42000,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// ── Tests ───────────────────────────────────────────────────────────────────
|
|
120
|
-
|
|
121
|
-
describe('pd pain record async mode (PRI-369)', () => {
|
|
122
|
-
beforeEach(() => {
|
|
123
|
-
vi.clearAllMocks();
|
|
124
|
-
mockRecordPainResult = { ...SUBMITTED_RESULT };
|
|
125
|
-
lastServiceOpts = null;
|
|
126
|
-
mockIsFeatureEnabledReturn = false;
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// 1. --json output is a single parseable JSON object in async mode
|
|
130
|
-
it('--json output is a single parseable JSON object with status=submitted', async () => {
|
|
131
|
-
mockIsFeatureEnabledReturn = true;
|
|
132
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
133
|
-
const exitSpy = mockProcessExit();
|
|
134
|
-
|
|
135
|
-
await handlePainRecord({ reason: 'test pain', json: true });
|
|
136
|
-
|
|
137
|
-
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
138
|
-
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]);
|
|
139
|
-
expect(jsonOutput.status).toBe('submitted');
|
|
140
|
-
expect(jsonOutput.painId).toBe('manual_123_abc');
|
|
141
|
-
expect(jsonOutput.taskId).toBe('diagnosis_manual_123_abc');
|
|
142
|
-
expect(jsonOutput.candidateIds).toEqual([]);
|
|
143
|
-
expect(jsonOutput.ledgerEntryIds).toEqual([]);
|
|
144
|
-
expect(jsonOutput.latencyMs).toBe(120);
|
|
145
|
-
expect(jsonOutput.message).toContain('pd task show');
|
|
146
|
-
expect(jsonOutput.reason).toContain('pd task show');
|
|
147
|
-
expect(jsonOutput.nextAction).toContain('pd diagnose run');
|
|
148
|
-
expect(jsonOutput.nextAction).toContain('--runtime pi-ai');
|
|
149
|
-
expect(jsonOutput.nextAction).toContain('--json');
|
|
150
|
-
// submitted should NOT cause exit(1)
|
|
151
|
-
expect(exitSpy).not.toHaveBeenCalledWith(1);
|
|
152
|
-
|
|
153
|
-
logSpy.mockRestore();
|
|
154
|
-
exitSpy.mockRestore();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// 2. submitted status outputs [SUBMITTED] in text mode
|
|
158
|
-
it('outputs [SUBMITTED] in text mode with no-consumer warning (PRI-570)', async () => {
|
|
159
|
-
mockIsFeatureEnabledReturn = true;
|
|
160
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
161
|
-
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
162
|
-
const exitSpy = mockProcessExit();
|
|
163
|
-
|
|
164
|
-
await handlePainRecord({ reason: 'test pain' });
|
|
165
|
-
|
|
166
|
-
const allOutput = logSpy.mock.calls.map(c => c.join(' ')).join(' ');
|
|
167
|
-
expect(allOutput).toContain('[SUBMITTED]');
|
|
168
|
-
expect(allOutput).toContain('submitted');
|
|
169
|
-
// PRI-570: the async path has NO automatic consumer — the warning must be
|
|
170
|
-
// explicit (rc-9), on stderr so it never pollutes piped stdout.
|
|
171
|
-
const errOutput = errSpy.mock.calls.map(c => c.join(' ')).join(' ');
|
|
172
|
-
expect(errOutput).toContain('no automatic consumer for diagnostician tasks');
|
|
173
|
-
expect(errOutput).toContain('pd diagnose run --task-id');
|
|
174
|
-
expect(errOutput).toContain('--wait');
|
|
175
|
-
expect(exitSpy).not.toHaveBeenCalledWith(1);
|
|
176
|
-
|
|
177
|
-
logSpy.mockRestore();
|
|
178
|
-
errSpy.mockRestore();
|
|
179
|
-
exitSpy.mockRestore();
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// 3. Flag off => legacy sync behavior unchanged
|
|
183
|
-
it('flag off => legacy sync behavior (asyncMode=false passed to service)', async () => {
|
|
184
|
-
// mockIsFeatureEnabledReturn defaults to false
|
|
185
|
-
mockRecordPainResult = { ...SUCCEEDED_RESULT };
|
|
186
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
187
|
-
const exitSpy = mockProcessExit();
|
|
188
|
-
|
|
189
|
-
await handlePainRecord({ reason: 'test pain', json: true });
|
|
190
|
-
|
|
191
|
-
// asyncMode should be false because the flag is off by default
|
|
192
|
-
expect(lastServiceOpts).toBeTruthy();
|
|
193
|
-
expect(lastServiceOpts!.asyncMode).toBe(false);
|
|
194
|
-
|
|
195
|
-
logSpy.mockRestore();
|
|
196
|
-
exitSpy.mockRestore();
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// 4. --wait overrides async flag to force sync
|
|
200
|
-
it('--wait overrides async flag to force sync (asyncMode=false even if flag on)', async () => {
|
|
201
|
-
mockIsFeatureEnabledReturn = true;
|
|
202
|
-
mockRecordPainResult = { ...SUCCEEDED_RESULT };
|
|
203
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
204
|
-
const exitSpy = mockProcessExit();
|
|
205
|
-
|
|
206
|
-
await handlePainRecord({ reason: 'test pain', wait: true, json: true });
|
|
207
|
-
|
|
208
|
-
// --wait should force asyncMode=false even though flag is on
|
|
209
|
-
expect(lastServiceOpts).toBeTruthy();
|
|
210
|
-
expect(lastServiceOpts!.asyncMode).toBe(false);
|
|
211
|
-
|
|
212
|
-
logSpy.mockRestore();
|
|
213
|
-
exitSpy.mockRestore();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// 5. asyncMode=true when flag is enabled and --wait is not set
|
|
217
|
-
it('asyncMode=true when flag enabled and --wait not set', async () => {
|
|
218
|
-
mockIsFeatureEnabledReturn = true;
|
|
219
|
-
mockRecordPainResult = { ...SUBMITTED_RESULT };
|
|
220
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
221
|
-
const exitSpy = mockProcessExit();
|
|
222
|
-
|
|
223
|
-
await handlePainRecord({ reason: 'test pain', json: true });
|
|
224
|
-
|
|
225
|
-
expect(lastServiceOpts).toBeTruthy();
|
|
226
|
-
expect(lastServiceOpts!.asyncMode).toBe(true);
|
|
227
|
-
|
|
228
|
-
logSpy.mockRestore();
|
|
229
|
-
exitSpy.mockRestore();
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// 6. Failed task-creation: no side effects (no spawn, no ledger writes)
|
|
233
|
-
it('failed task-creation produces no candidateIds or ledgerEntryIds', async () => {
|
|
234
|
-
mockRecordPainResult = {
|
|
235
|
-
status: 'failed',
|
|
236
|
-
painId: 'manual_123_abc',
|
|
237
|
-
taskId: 'diagnosis_manual_123_abc',
|
|
238
|
-
candidateIds: [],
|
|
239
|
-
ledgerEntryIds: [],
|
|
240
|
-
observabilityWarnings: [],
|
|
241
|
-
failureCategory: 'runtime_unavailable' as FailureCategory,
|
|
242
|
-
latencyMs: 5,
|
|
243
|
-
message: 'Task creation failed',
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
247
|
-
const exitSpy = mockProcessExit();
|
|
248
|
-
|
|
249
|
-
await handlePainRecord({ reason: 'test pain', json: true });
|
|
250
|
-
|
|
251
|
-
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]);
|
|
252
|
-
expect(jsonOutput.candidateIds).toEqual([]);
|
|
253
|
-
expect(jsonOutput.ledgerEntryIds).toEqual([]);
|
|
254
|
-
expect(jsonOutput.status).toBe('failed');
|
|
255
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
256
|
-
|
|
257
|
-
logSpy.mockRestore();
|
|
258
|
-
exitSpy.mockRestore();
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// 7. Stubbed process.exit(1) does not cause side effects in JSON mode
|
|
262
|
-
it('stubbed process.exit(1) in JSON mode does not execute else branch', async () => {
|
|
263
|
-
mockRecordPainResult = {
|
|
264
|
-
status: 'failed',
|
|
265
|
-
painId: 'manual_123_abc',
|
|
266
|
-
taskId: 'diagnosis_manual_123_abc',
|
|
267
|
-
candidateIds: ['c1'],
|
|
268
|
-
ledgerEntryIds: ['l1'],
|
|
269
|
-
observabilityWarnings: [],
|
|
270
|
-
failureCategory: 'runtime_unavailable' as FailureCategory,
|
|
271
|
-
latencyMs: 5,
|
|
272
|
-
message: 'Task creation failed',
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
276
|
-
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
277
|
-
const exitSpy = mockProcessExit();
|
|
278
|
-
|
|
279
|
-
await handlePainRecord({ reason: 'test pain', json: true });
|
|
280
|
-
|
|
281
|
-
// Verify JSON output was printed
|
|
282
|
-
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
283
|
-
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]);
|
|
284
|
-
expect(jsonOutput.status).toBe('failed');
|
|
285
|
-
expect(jsonOutput.candidateIds).toEqual(['c1']);
|
|
286
|
-
|
|
287
|
-
// Verify process.exit(1) was called
|
|
288
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
289
|
-
|
|
290
|
-
// Verify else branch was NOT executed (no error.log for [FAIL] message)
|
|
291
|
-
expect(errorSpy).not.toHaveBeenCalled();
|
|
292
|
-
|
|
293
|
-
logSpy.mockRestore();
|
|
294
|
-
errorSpy.mockRestore();
|
|
295
|
-
exitSpy.mockRestore();
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
// 8. Stubbed process.exit(1) does not cause side effects in text mode
|
|
299
|
-
it('stubbed process.exit(1) in text mode stops after error message', async () => {
|
|
300
|
-
mockRecordPainResult = {
|
|
301
|
-
status: 'failed',
|
|
302
|
-
painId: 'manual_123_abc',
|
|
303
|
-
taskId: 'diagnosis_manual_123_abc',
|
|
304
|
-
candidateIds: ['c1'],
|
|
305
|
-
ledgerEntryIds: ['l1'],
|
|
306
|
-
observabilityWarnings: [],
|
|
307
|
-
failureCategory: 'runtime_unavailable' as FailureCategory,
|
|
308
|
-
latencyMs: 5,
|
|
309
|
-
message: 'Test failure message',
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
313
|
-
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
314
|
-
const exitSpy = mockProcessExit();
|
|
315
|
-
|
|
316
|
-
await handlePainRecord({ reason: 'test pain' }); // Not json mode
|
|
317
|
-
|
|
318
|
-
// Verify [FAIL] error message was printed
|
|
319
|
-
expect(errorSpy).toHaveBeenCalledWith('[FAIL] Pain signal failed:', 'Test failure message');
|
|
320
|
-
|
|
321
|
-
// Verify process.exit(1) was called
|
|
322
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
323
|
-
|
|
324
|
-
// Verify no success/submitted/skip/retry messages were printed after exit
|
|
325
|
-
const allLogMessages = logSpy.mock.calls.flat();
|
|
326
|
-
const forbiddenPatterns = ['[OK]', '[SUBMITTED]', '[SKIP]', '[RETRY]'];
|
|
327
|
-
for (const pattern of forbiddenPatterns) {
|
|
328
|
-
expect(allLogMessages).not.toEqual(expect.stringContaining(pattern));
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
logSpy.mockRestore();
|
|
332
|
-
errorSpy.mockRestore();
|
|
333
|
-
exitSpy.mockRestore();
|
|
334
|
-
});
|
|
335
|
-
});
|