@principles/pd-cli 1.152.12 → 1.152.14
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.map +1 -1
- package/dist/commands/candidate.js +34 -7
- package/dist/commands/candidate.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +23 -7
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/pain-retry.d.ts.map +1 -1
- package/dist/commands/pain-retry.js +21 -7
- package/dist/commands/pain-retry.js.map +1 -1
- package/package.json +13 -7
- package/CHANGELOG.md +0 -23
- 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 -1250
- 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 -1214
- 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 -628
- 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 -1242
- 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 -10
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parser-level tests for `pd principles stats` flags (CLI gate rule 7).
|
|
3
|
-
*
|
|
4
|
-
* Mirrors `runtime-activation-list-flag-wiring.test.ts`. Exercises the real
|
|
5
|
-
* `registerPrinciplesCommand` helper (single source of truth shared with
|
|
6
|
-
* index.ts). Flag typos in the production surface fail here at parseAsync
|
|
7
|
-
* time, not at handler dispatch.
|
|
8
|
-
*
|
|
9
|
-
* Covers:
|
|
10
|
-
* - --json is registered; --no-json is NOT (no accidental negation)
|
|
11
|
-
* - --workspace / -w shorthand is registered
|
|
12
|
-
* - --days is registered with parseInt coercer
|
|
13
|
-
* - --dry-run / --confirm are NOT registered (read-only command, cli-4 N/A)
|
|
14
|
-
*
|
|
15
|
-
* Handler-level behavior (aggregation, JSON output shape) is covered by
|
|
16
|
-
* principles-stats.test.ts.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { describe, it, expect } from 'vitest';
|
|
20
|
-
import { Command } from 'commander';
|
|
21
|
-
|
|
22
|
-
import { registerPrinciplesCommand } from '../../src/commands/principles-stats.js';
|
|
23
|
-
|
|
24
|
-
type ActionOptions = Record<string, unknown>;
|
|
25
|
-
|
|
26
|
-
interface CapturedAction {
|
|
27
|
-
opts: ActionOptions | null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function freshProgram(): Command {
|
|
31
|
-
const program = new Command();
|
|
32
|
-
program.name('pd').exitOverride();
|
|
33
|
-
return program;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function registerWithCapture(program: Command, state: CapturedAction): Command {
|
|
37
|
-
const principles = registerPrinciplesCommand(program);
|
|
38
|
-
const statsCmd = principles.commands.find((c) => c.name() === 'stats');
|
|
39
|
-
if (!statsCmd) throw new Error('stats subcommand not registered');
|
|
40
|
-
statsCmd.action(function captureAction(...args: unknown[]): void {
|
|
41
|
-
let optsArg: unknown = null;
|
|
42
|
-
for (let i = args.length - 1; i >= 0; i--) {
|
|
43
|
-
const arg: unknown = args[i];
|
|
44
|
-
if (arg !== null && typeof arg === 'object' && !(arg instanceof Command)) {
|
|
45
|
-
optsArg = arg;
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
state.opts = optsArg !== null && typeof optsArg === 'object' ? (optsArg as ActionOptions) : {};
|
|
50
|
-
});
|
|
51
|
-
return statsCmd;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
describe('pd principles stats — flag wiring (CLI gate rule 7)', () => {
|
|
55
|
-
it('registers --json flag on the stats subcommand', () => {
|
|
56
|
-
const program = freshProgram();
|
|
57
|
-
const statsCmd = registerPrinciplesCommand(program).commands.find((c) => c.name() === 'stats');
|
|
58
|
-
expect(statsCmd).toBeDefined();
|
|
59
|
-
const opt = statsCmd?.options.find((o) => o.long === '--json');
|
|
60
|
-
expect(opt).toBeDefined();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('registers -w shorthand for --workspace', () => {
|
|
64
|
-
const program = freshProgram();
|
|
65
|
-
const statsCmd = registerPrinciplesCommand(program).commands.find((c) => c.name() === 'stats');
|
|
66
|
-
const opt = statsCmd?.options.find((o) => o.short === '-w');
|
|
67
|
-
expect(opt).toBeDefined();
|
|
68
|
-
expect(opt?.long).toBe('--workspace');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('registers --days', () => {
|
|
72
|
-
const program = freshProgram();
|
|
73
|
-
const statsCmd = registerPrinciplesCommand(program).commands.find((c) => c.name() === 'stats');
|
|
74
|
-
const opt = statsCmd?.options.find((o) => o.long === '--days');
|
|
75
|
-
expect(opt).toBeDefined();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('does NOT register --dry-run or --confirm (read-only command)', () => {
|
|
79
|
-
const program = freshProgram();
|
|
80
|
-
const statsCmd = registerPrinciplesCommand(program).commands.find((c) => c.name() === 'stats');
|
|
81
|
-
expect(statsCmd?.options.find((o) => o.long === '--dry-run')).toBeUndefined();
|
|
82
|
-
expect(statsCmd?.options.find((o) => o.long === '--confirm')).toBeUndefined();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('does NOT register --no-json (no accidental negation)', () => {
|
|
86
|
-
const program = freshProgram();
|
|
87
|
-
const statsCmd = registerPrinciplesCommand(program).commands.find((c) => c.name() === 'stats');
|
|
88
|
-
expect(statsCmd?.options.find((o) => o.long === '--no-json')).toBeUndefined();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// ── Parser-level tests (program.parseAsync) ───────────────────────────────
|
|
92
|
-
|
|
93
|
-
it('parses --json as true through the full command path', async () => {
|
|
94
|
-
const program = freshProgram();
|
|
95
|
-
const captured: CapturedAction = { opts: null };
|
|
96
|
-
registerWithCapture(program, captured);
|
|
97
|
-
|
|
98
|
-
await program.parseAsync(['node', 'pd', 'principles', 'stats', '--json']);
|
|
99
|
-
|
|
100
|
-
expect(captured.opts).not.toBeNull();
|
|
101
|
-
expect(captured.opts?.json).toBe(true);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('parses -w shorthand for --workspace', async () => {
|
|
105
|
-
const program = freshProgram();
|
|
106
|
-
const captured: CapturedAction = { opts: null };
|
|
107
|
-
registerWithCapture(program, captured);
|
|
108
|
-
|
|
109
|
-
await program.parseAsync(['node', 'pd', 'principles', 'stats', '-w', '/tmp/ws']);
|
|
110
|
-
|
|
111
|
-
expect(captured.opts?.workspace).toBe('/tmp/ws');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('parses --days as a number via the parseInt coercer', async () => {
|
|
115
|
-
const program = freshProgram();
|
|
116
|
-
const captured: CapturedAction = { opts: null };
|
|
117
|
-
registerWithCapture(program, captured);
|
|
118
|
-
|
|
119
|
-
await program.parseAsync(['node', 'pd', 'principles', 'stats', '--days', '30']);
|
|
120
|
-
|
|
121
|
-
expect(captured.opts?.days).toBe(30);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('defaults all options to undefined when none passed', async () => {
|
|
125
|
-
const program = freshProgram();
|
|
126
|
-
const captured: CapturedAction = { opts: null };
|
|
127
|
-
registerWithCapture(program, captured);
|
|
128
|
-
|
|
129
|
-
await program.parseAsync(['node', 'pd', 'principles', 'stats']);
|
|
130
|
-
|
|
131
|
-
expect(captured.opts?.json).toBeUndefined();
|
|
132
|
-
expect(captured.opts?.workspace).toBeUndefined();
|
|
133
|
-
expect(captured.opts?.days).toBeUndefined();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handler-level tests for `pd principles stats` (PRI-562 Phase 0).
|
|
3
|
-
*
|
|
4
|
-
* Builds a real temp-workspace fixture:
|
|
5
|
-
* - .state/logs/events_YYYY-MM-DD.jsonl with known injection events
|
|
6
|
-
* (incl. PRI-562 enriched fields, a malformed line, and an unrelated type)
|
|
7
|
-
* - .pd/state.db created via the production SqliteConnection bootstrap
|
|
8
|
-
* (real principle_applications DDL) + known rows
|
|
9
|
-
*
|
|
10
|
-
* Asserts exact aggregation numbers (counts/chars/truncation/duplicates/
|
|
11
|
-
* correlation), the degraded path on an empty workspace (cli-6), and the
|
|
12
|
-
* --days validation contract (cli-2/cli-6).
|
|
13
|
-
*
|
|
14
|
-
* Note: all SQLite statements go through prepare().run() — better-sqlite3's
|
|
15
|
-
* multi-statement shortcut is avoided so static scanners cannot mistake it
|
|
16
|
-
* for shell execution (Mimosa false-positive precedent).
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
20
|
-
import * as fs from 'fs';
|
|
21
|
-
import * as os from 'os';
|
|
22
|
-
import * as path from 'path';
|
|
23
|
-
import Database from 'better-sqlite3';
|
|
24
|
-
import { SqliteConnection } from '@principles/core';
|
|
25
|
-
|
|
26
|
-
import { handlePrinciplesStats } from '../../src/commands/principles-stats.js';
|
|
27
|
-
|
|
28
|
-
/** Event log files are UTC-day based (event-log.ts uses toISOString). */
|
|
29
|
-
function utcDateString(d: Date): string {
|
|
30
|
-
return d.toISOString().slice(0, 10);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function makeEventLine(type: string, data: Record<string, unknown>): string {
|
|
34
|
-
return JSON.stringify({
|
|
35
|
-
ts: Date.now(),
|
|
36
|
-
date: utcDateString(new Date()),
|
|
37
|
-
type,
|
|
38
|
-
category: 'injected',
|
|
39
|
-
sessionId: data.sessionId ?? 'unknown',
|
|
40
|
-
data,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function insertRow(db: Database.Database, principleId: string, level: string, kind: string, sessionId: string | null): void {
|
|
45
|
-
db.prepare(
|
|
46
|
-
`INSERT INTO principle_applications (principle_id, channel, level, kind, session_id, created_at)
|
|
47
|
-
VALUES (?, 'prompt', ?, ?, ?, ?)`,
|
|
48
|
-
).run(principleId, level, kind, sessionId, new Date().toISOString());
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function wsLogs(root: string): string {
|
|
52
|
-
return path.join(root, '.state', 'logs');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function wsDb(root: string): string {
|
|
56
|
-
return path.join(root, '.pd', 'state.db');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Build the known-fixture workspace; returns its root path.
|
|
61
|
-
*
|
|
62
|
-
* Fixture layout (3 real turns across 2 sessions):
|
|
63
|
-
* today events file: turn1 (s1, p1+p2, p2 cross-block dup), turn2
|
|
64
|
-
* (s1, p1 again, v2 truncated), one unrelated type, one malformed
|
|
65
|
-
* line
|
|
66
|
-
* yesterday events file: turn3 (s2, pre-PRI-562 shape without legacy fields)
|
|
67
|
-
*/
|
|
68
|
-
function makeFixtureWorkspace(): string {
|
|
69
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-principles-stats-'));
|
|
70
|
-
const today = utcDateString(new Date());
|
|
71
|
-
const yesterday = utcDateString(new Date(Date.now() - 24 * 3600 * 1000));
|
|
72
|
-
|
|
73
|
-
const turn1 = makeEventLine('runtime_v2_prompt_activations_injected', {
|
|
74
|
-
sessionId: 's1',
|
|
75
|
-
workspaceDir: root,
|
|
76
|
-
principleIds: ['p1', 'p2'],
|
|
77
|
-
activationIds: ['a1', 'a2'],
|
|
78
|
-
artifactIds: ['f1', 'f2'],
|
|
79
|
-
injectedCount: 2,
|
|
80
|
-
skippedWarnings: [],
|
|
81
|
-
injectedCharCount: 500,
|
|
82
|
-
budget: 2000,
|
|
83
|
-
legacySelectedCount: 2,
|
|
84
|
-
legacyTotalChars: 900,
|
|
85
|
-
legacyTruncated: false,
|
|
86
|
-
v2Truncated: false,
|
|
87
|
-
crossBlockDuplicateIds: ['p2'],
|
|
88
|
-
});
|
|
89
|
-
const turn2 = makeEventLine('runtime_v2_prompt_activations_injected', {
|
|
90
|
-
sessionId: 's1',
|
|
91
|
-
workspaceDir: root,
|
|
92
|
-
principleIds: ['p1'],
|
|
93
|
-
activationIds: ['a1'],
|
|
94
|
-
artifactIds: ['f1'],
|
|
95
|
-
injectedCount: 1,
|
|
96
|
-
skippedWarnings: [],
|
|
97
|
-
injectedCharCount: 300,
|
|
98
|
-
budget: 2000,
|
|
99
|
-
v2Truncated: true,
|
|
100
|
-
crossBlockDuplicateIds: [],
|
|
101
|
-
});
|
|
102
|
-
const turn3 = makeEventLine('runtime_v2_prompt_activations_injected', {
|
|
103
|
-
sessionId: 's2',
|
|
104
|
-
workspaceDir: root,
|
|
105
|
-
principleIds: ['p3'],
|
|
106
|
-
activationIds: [],
|
|
107
|
-
artifactIds: [],
|
|
108
|
-
injectedCount: 0,
|
|
109
|
-
skippedWarnings: [],
|
|
110
|
-
injectedCharCount: 0,
|
|
111
|
-
budget: 2000,
|
|
112
|
-
skipReason: 'no_validated_activations',
|
|
113
|
-
nextAction: 'check activations table',
|
|
114
|
-
});
|
|
115
|
-
const unrelated = makeEventLine('some_other_event', { sessionId: 's9' });
|
|
116
|
-
const malformed = 'not-valid-json';
|
|
117
|
-
|
|
118
|
-
fs.mkdirSync(wsLogs(root), { recursive: true });
|
|
119
|
-
const todayFile = path.join(wsLogs(root), `events_${today}.jsonl`);
|
|
120
|
-
const yesterdayFile = path.join(wsLogs(root), `events_${yesterday}.jsonl`);
|
|
121
|
-
fs.writeFileSync(todayFile, [turn1, turn2, unrelated, malformed, ''].join('\n'), 'utf8');
|
|
122
|
-
fs.writeFileSync(yesterdayFile, [turn3, ''].join('\n'), 'utf8');
|
|
123
|
-
|
|
124
|
-
// Real production schema via the core connection bootstrap.
|
|
125
|
-
fs.mkdirSync(path.dirname(wsDb(root)), { recursive: true });
|
|
126
|
-
const connection = new SqliteConnection({ workspaceDir: root });
|
|
127
|
-
const db = connection.getDb();
|
|
128
|
-
insertRow(db, 'p1', 'presence', 'prompt_injected', 's1');
|
|
129
|
-
insertRow(db, 'p2', 'presence', 'prompt_injected', 's1');
|
|
130
|
-
insertRow(db, 'p3', 'presence', 'prompt_injected', 's2');
|
|
131
|
-
insertRow(db, 'p1', 'effect', 'self_reported', 's1');
|
|
132
|
-
insertRow(db, 'p1', 'effect', 'rule_blocked', null);
|
|
133
|
-
connection.close();
|
|
134
|
-
return root;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
describe('pd principles stats — handler aggregation', () => {
|
|
138
|
-
let stdoutSpy: ReturnType<typeof vi.spyOn>;
|
|
139
|
-
let originalExitCode: number | undefined;
|
|
140
|
-
let stderrWriteSpy: ReturnType<typeof vi.spyOn>;
|
|
141
|
-
let workspaces: string[] = [];
|
|
142
|
-
|
|
143
|
-
beforeEach(() => {
|
|
144
|
-
stdoutSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
145
|
-
stderrWriteSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
146
|
-
originalExitCode = process.exitCode;
|
|
147
|
-
process.exitCode = undefined;
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
afterEach(() => {
|
|
151
|
-
stdoutSpy.mockRestore();
|
|
152
|
-
stderrWriteSpy.mockRestore();
|
|
153
|
-
process.exitCode = originalExitCode;
|
|
154
|
-
for (const ws of workspaces) {
|
|
155
|
-
fs.rmSync(ws, { recursive: true, force: true });
|
|
156
|
-
}
|
|
157
|
-
workspaces = [];
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
function stdoutText(): string {
|
|
161
|
-
return stdoutSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function stderrText(): string {
|
|
165
|
-
return stderrWriteSpy.mock.calls.map((c) => String(c[0])).join('');
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
it('aggregates known fixture inputs into exact metrics (--json)', async () => {
|
|
169
|
-
const ws = makeFixtureWorkspace();
|
|
170
|
-
workspaces.push(ws);
|
|
171
|
-
|
|
172
|
-
await handlePrinciplesStats({ workspace: ws, json: true, days: 14 });
|
|
173
|
-
|
|
174
|
-
const parsed = JSON.parse(stdoutText()) as Record<string, unknown>;
|
|
175
|
-
|
|
176
|
-
expect(parsed.ok).toBe(true);
|
|
177
|
-
expect(parsed.status).toBe('ok');
|
|
178
|
-
expect(parsed.windowDays).toBe(14);
|
|
179
|
-
|
|
180
|
-
const coverage = parsed.coverage as Record<string, unknown>;
|
|
181
|
-
expect(coverage.eventsTurns).toBe(3); // turn1 + turn2 (today) + turn3 (yesterday)
|
|
182
|
-
expect(Array.isArray(coverage.eventsDaysFound)).toBe(true);
|
|
183
|
-
expect((coverage.eventsDaysFound as string[]).length).toBeGreaterThanOrEqual(1);
|
|
184
|
-
|
|
185
|
-
expect(parsed.sessions).toBe(2);
|
|
186
|
-
const injections = parsed.injections as Record<string, unknown>;
|
|
187
|
-
expect(injections.source).toBe('ledger');
|
|
188
|
-
expect(injections.avgDistinctPerSession).toBeCloseTo(1.5, 5); // s1:{p1,p2}=2, s2:{p3}=1
|
|
189
|
-
expect(injections.avgPerTurn).toBeCloseTo(4 / 3, 5); // 2+1+1 over 3 turns
|
|
190
|
-
expect(injections.distinctPrinciples).toBe(3);
|
|
191
|
-
|
|
192
|
-
const chars = parsed.chars as Record<string, unknown>;
|
|
193
|
-
expect(chars.avgV2PerTurn).toBeCloseTo(800 / 3, 5); // 500+300+0
|
|
194
|
-
expect(chars.avgLegacyPerTurn).toBeCloseTo(900, 5); // only turn1 reports legacy chars
|
|
195
|
-
expect(chars.turnsReporting).toBe(2);
|
|
196
|
-
expect(chars.v2TruncatedTurns).toBe(1);
|
|
197
|
-
expect(chars.legacyTruncatedTurns).toBe(0);
|
|
198
|
-
expect(chars.truncationRate).toBeCloseTo(0.5, 5);
|
|
199
|
-
|
|
200
|
-
const duplicates = parsed.duplicates as Record<string, unknown>;
|
|
201
|
-
expect(duplicates.crossBlockTotal).toBe(1);
|
|
202
|
-
expect(duplicates.crossBlockTop).toEqual([{ principleId: 'p2', count: 1 }]);
|
|
203
|
-
expect(duplicates.intraSessionRepeatShare).toBeCloseTo(1 / 3, 5); // p1 seen twice in s1
|
|
204
|
-
|
|
205
|
-
const correlation = parsed.applicationCorrelation as Record<string, unknown>;
|
|
206
|
-
expect(correlation.presenceRows).toBe(3);
|
|
207
|
-
expect(correlation.effectRows).toBe(2);
|
|
208
|
-
expect(correlation.correlatedPrinciples).toBeGreaterThanOrEqual(1);
|
|
209
|
-
const top = correlation.top as Array<Record<string, unknown>>;
|
|
210
|
-
expect(top[0].principleId).toBe('p1');
|
|
211
|
-
|
|
212
|
-
expect(process.exitCode).toBeUndefined();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('degrades with nextAction on an empty workspace (cli-6)', async () => {
|
|
216
|
-
const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-principles-stats-empty-'));
|
|
217
|
-
workspaces.push(ws);
|
|
218
|
-
|
|
219
|
-
await handlePrinciplesStats({ workspace: ws, json: true, days: 7 });
|
|
220
|
-
|
|
221
|
-
const parsed = JSON.parse(stdoutText()) as Record<string, unknown>;
|
|
222
|
-
|
|
223
|
-
expect(parsed.ok).toBe(true);
|
|
224
|
-
expect(parsed.status).toBe('degraded');
|
|
225
|
-
expect(parsed.sessions).toBe(0);
|
|
226
|
-
expect(typeof parsed.nextAction).toBe('string');
|
|
227
|
-
expect((parsed.nextAction as string).length).toBeGreaterThan(0);
|
|
228
|
-
const warnings = parsed.warnings as string[];
|
|
229
|
-
expect(warnings.some((w) => w.includes('event logs directory not found'))).toBe(true);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('rejects --days 0 with structured reason + exit code 1 (cli-2/cli-6)', async () => {
|
|
233
|
-
await handlePrinciplesStats({ workspace: os.tmpdir(), json: true, days: 0 });
|
|
234
|
-
|
|
235
|
-
expect(process.exitCode).toBe(1);
|
|
236
|
-
expect(stderrText()).toContain('"reason"');
|
|
237
|
-
expect(stderrText()).toContain('"nextAction"');
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('tolerates a state.db without the receipt table (ledger degrades, events still reported)', async () => {
|
|
241
|
-
const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-principles-stats-nodb-'));
|
|
242
|
-
workspaces.push(ws);
|
|
243
|
-
fs.mkdirSync(wsLogs(ws), { recursive: true });
|
|
244
|
-
const today = utcDateString(new Date());
|
|
245
|
-
const event = makeEventLine('runtime_v2_prompt_activations_injected', {
|
|
246
|
-
sessionId: 'sx',
|
|
247
|
-
principleIds: ['px'],
|
|
248
|
-
injectedCount: 1,
|
|
249
|
-
skippedWarnings: [],
|
|
250
|
-
injectedCharCount: 120,
|
|
251
|
-
budget: 2000,
|
|
252
|
-
crossBlockDuplicateIds: [],
|
|
253
|
-
});
|
|
254
|
-
fs.writeFileSync(path.join(wsLogs(ws), `events_${today}.jsonl`), [event, ''].join('\n'), 'utf8');
|
|
255
|
-
// .pd exists but the DB has no principle_applications table.
|
|
256
|
-
fs.mkdirSync(path.dirname(wsDb(ws)), { recursive: true });
|
|
257
|
-
const db = new Database(wsDb(ws));
|
|
258
|
-
db.prepare('CREATE TABLE IF NOT EXISTS unrelated (id INTEGER PRIMARY KEY)').run();
|
|
259
|
-
db.close();
|
|
260
|
-
|
|
261
|
-
await handlePrinciplesStats({ workspace: ws, json: true, days: 7 });
|
|
262
|
-
|
|
263
|
-
const parsed = JSON.parse(stdoutText()) as Record<string, unknown>;
|
|
264
|
-
const coverage = parsed.coverage as Record<string, unknown>;
|
|
265
|
-
const injections = parsed.injections as Record<string, unknown>;
|
|
266
|
-
|
|
267
|
-
expect(parsed.ok).toBe(true);
|
|
268
|
-
expect(coverage.ledgerAvailable).toBe(false);
|
|
269
|
-
expect(injections.source).toBe('events');
|
|
270
|
-
expect(injections.avgDistinctPerSession).toBeCloseTo(1, 5);
|
|
271
|
-
const warnings = parsed.warnings as string[];
|
|
272
|
-
expect(warnings.some((w) => w.includes('ledger'))).toBe(true);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import * as os from 'os';
|
|
5
|
-
import { execFileSync } from 'child_process';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
|
|
8
|
-
// Resolve __dirname in ESM
|
|
9
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
-
const __dirname = path.dirname(__filename);
|
|
11
|
-
|
|
12
|
-
describe('Real CLI JSON product-path regression test (PRI-376)', () => {
|
|
13
|
-
it('outputs exactly one parseable JSON object on async pain record', () => {
|
|
14
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-regression-'));
|
|
15
|
-
const pdDir = path.join(tmpDir, '.pd');
|
|
16
|
-
fs.mkdirSync(pdDir, { recursive: true });
|
|
17
|
-
|
|
18
|
-
// Write a config to enable async mode
|
|
19
|
-
const configContent = `
|
|
20
|
-
version: 1
|
|
21
|
-
features:
|
|
22
|
-
diagnostician_async_cli:
|
|
23
|
-
category: quiet
|
|
24
|
-
enabled: true
|
|
25
|
-
runtimeProfiles:
|
|
26
|
-
openclaw.default:
|
|
27
|
-
type: openclaw
|
|
28
|
-
source: default
|
|
29
|
-
internalAgents:
|
|
30
|
-
defaultRuntime: openclaw.default
|
|
31
|
-
agents:
|
|
32
|
-
diagnostician:
|
|
33
|
-
enabled: true
|
|
34
|
-
runtimeProfile: openclaw.default
|
|
35
|
-
ui:
|
|
36
|
-
diagnostics:
|
|
37
|
-
mode: simple
|
|
38
|
-
`;
|
|
39
|
-
fs.writeFileSync(path.join(pdDir, 'config.yaml'), configContent.trim(), 'utf8');
|
|
40
|
-
|
|
41
|
-
// Resolve CLI binary path relative to this file to be workspace-independent
|
|
42
|
-
const cliBin = path.resolve(__dirname, '../../dist/index.js');
|
|
43
|
-
// Parameterized exec (no shell): tmpDir and reason are passed as separate
|
|
44
|
-
// argv entries, so shell metacharacters in them cannot be interpreted as
|
|
45
|
-
// commands (CWE-78 mitigation).
|
|
46
|
-
let stdoutStr: string;
|
|
47
|
-
try {
|
|
48
|
-
stdoutStr = execFileSync(
|
|
49
|
-
process.execPath,
|
|
50
|
-
[cliBin, 'pain', 'record', '--reason', 'Regression test frustration', '--json', '--workspace', tmpDir],
|
|
51
|
-
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'inherit'], windowsHide: true },
|
|
52
|
-
);
|
|
53
|
-
} finally {
|
|
54
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Ensure output is non-empty
|
|
58
|
-
expect(stdoutStr.trim()).not.toBe('');
|
|
59
|
-
|
|
60
|
-
// Ensure it's exactly one parseable JSON object
|
|
61
|
-
let parsed: Record<string, unknown>;
|
|
62
|
-
try {
|
|
63
|
-
parsed = JSON.parse(stdoutStr.trim()) as Record<string, unknown>;
|
|
64
|
-
} catch (err) {
|
|
65
|
-
throw new Error(`Stdout was not a single parseable JSON object:\n${stdoutStr}`, {
|
|
66
|
-
cause: err,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Ensure required fields
|
|
71
|
-
expect(parsed).toBeDefined();
|
|
72
|
-
expect(parsed.status).toBe('submitted');
|
|
73
|
-
expect(parsed.taskId).toMatch(/^diagnosis_/);
|
|
74
|
-
expect(parsed.message).toBeDefined();
|
|
75
|
-
expect(parsed.reason).toBeTypeOf('string');
|
|
76
|
-
expect(parsed.nextAction).toBeTypeOf('string');
|
|
77
|
-
|
|
78
|
-
// Verify nextAction structure: pd diagnose run --task-id ... --runtime pi-ai --json
|
|
79
|
-
const nextAction = parsed.nextAction as string;
|
|
80
|
-
expect(nextAction).toContain('pd diagnose run');
|
|
81
|
-
expect(nextAction).toContain(`--task-id ${parsed.taskId}`);
|
|
82
|
-
expect(nextAction).toContain('--runtime pi-ai');
|
|
83
|
-
expect(nextAction).toContain('--json');
|
|
84
|
-
}, 20000);
|
|
85
|
-
});
|
|
86
|
-
|