@principles/pd-cli 1.152.5 → 1.152.7
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/build-trajectory-evidence.d.ts +10 -0
- package/dist/commands/build-trajectory-evidence.d.ts.map +1 -1
- package/dist/commands/build-trajectory-evidence.js +12 -0
- package/dist/commands/build-trajectory-evidence.js.map +1 -1
- package/dist/commands/pain-record.d.ts.map +1 -1
- package/dist/commands/pain-record.js +63 -29
- package/dist/commands/pain-record.js.map +1 -1
- package/dist/commands/runtime-init.d.ts.map +1 -1
- package/dist/commands/runtime-init.js +4 -6
- package/dist/commands/runtime-init.js.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.js +21 -1
- package/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
- package/dist/services/demo-rule-compiler.d.ts +9 -11
- package/dist/services/demo-rule-compiler.d.ts.map +1 -1
- package/dist/services/demo-rule-compiler.js +11 -37
- package/dist/services/demo-rule-compiler.js.map +1 -1
- package/dist/services/rulehost-pipeline-runner.d.ts +6 -9
- package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/dist/services/rulehost-pipeline-runner.js +6 -9
- package/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/build-trajectory-evidence.ts +22 -0
- package/src/commands/pain-record.ts +64 -29
- package/src/commands/runtime-init.ts +4 -6
- package/src/commands/runtime-internalization-run-rulehost.ts +22 -1
- package/src/services/demo-rule-compiler.ts +12 -45
- package/src/services/rulehost-pipeline-runner.ts +6 -9
- package/tests/commands/build-trajectory-evidence.test.ts +106 -0
- package/tests/commands/pain-record-session-parser.test.ts +62 -12
- package/tests/commands/pain-record.test.ts +59 -19
|
@@ -566,6 +566,7 @@ describe('pd pain record', () => {
|
|
|
566
566
|
status: 'unavailable',
|
|
567
567
|
reasonCode: 'session_not_found',
|
|
568
568
|
detail: 'session not present in trajectory.db sessions table',
|
|
569
|
+
binding: 'unverified',
|
|
569
570
|
} as any);
|
|
570
571
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
571
572
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
@@ -579,6 +580,9 @@ describe('pd pain record', () => {
|
|
|
579
580
|
expect(jsonOutput.status).toBe('failed');
|
|
580
581
|
expect(jsonOutput.reason).toBe('session_not_found');
|
|
581
582
|
expect(jsonOutput.nextAction).toBeTruthy();
|
|
583
|
+
// PRI-783: the next action must not recommend retrying the exact path
|
|
584
|
+
// that just failed (`pd pain record --session`).
|
|
585
|
+
expect(jsonOutput.nextAction).not.toMatch(/pd pain record --session/);
|
|
582
586
|
// cli-2/cli-5: execution stopped — no service mutation, exit 1
|
|
583
587
|
expect(lastRecordPainInput).toBeNull();
|
|
584
588
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
@@ -593,6 +597,7 @@ describe('pd pain record', () => {
|
|
|
593
597
|
status: 'unavailable',
|
|
594
598
|
reasonCode: 'session_not_found',
|
|
595
599
|
detail: 'session not present',
|
|
600
|
+
binding: 'unverified',
|
|
596
601
|
} as any);
|
|
597
602
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
598
603
|
const exitSpy = mockProcessExit();
|
|
@@ -607,70 +612,105 @@ describe('pd pain record', () => {
|
|
|
607
612
|
exitSpy.mockRestore();
|
|
608
613
|
});
|
|
609
614
|
|
|
610
|
-
it('
|
|
611
|
-
//
|
|
612
|
-
//
|
|
613
|
-
// (
|
|
614
|
-
//
|
|
615
|
+
it('degrades to a bound, honest empty-evidence submission on empty_trajectory (PRI-783: a verified session must not be refused)', async () => {
|
|
616
|
+
// SPEC §8.2: binding failure and evidence failure are separate axes — a
|
|
617
|
+
// real session with no usable evidence rows is bound + unavailable
|
|
618
|
+
// (row 4, degrade), not unbound (row 5, refuse). PRI-783 Owner
|
|
619
|
+
// directive: a correct current session id is sufficient; empty evidence
|
|
620
|
+
// must not hard-refuse the report.
|
|
615
621
|
vi.mocked(acquireTrajectoryEvidenceFromDb).mockReturnValueOnce({
|
|
616
622
|
status: 'unavailable',
|
|
617
623
|
reasonCode: 'empty_trajectory',
|
|
618
624
|
detail: 'session exists but no usable evidence',
|
|
625
|
+
binding: 'verified',
|
|
619
626
|
} as any);
|
|
620
627
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
621
628
|
const exitSpy = mockProcessExit();
|
|
622
629
|
|
|
623
630
|
await handlePainRecord({ reason: 'test pain', session: 'quiet-session', json: true });
|
|
624
631
|
|
|
625
|
-
|
|
632
|
+
// Bound correlation with honest empty evidence — nothing fabricated.
|
|
633
|
+
expect(lastRecordPainInput).not.toBeNull();
|
|
634
|
+
expect(lastRecordPainInput!.sessionId).toBe('quiet-session');
|
|
635
|
+
expect(lastRecordPainInput!.hostKind).toBe('openclaw');
|
|
636
|
+
expect(lastRecordPainInput!.evidence).toEqual([]);
|
|
637
|
+
expect(lastRecordPainInput!.painIngress.correlation.status).toBe('bound');
|
|
638
|
+
expect(lastRecordPainInput!.painIngress.evidenceClass).toEqual({
|
|
639
|
+
status: 'unavailable',
|
|
640
|
+
reason: 'empty_trajectory',
|
|
641
|
+
});
|
|
642
|
+
// Degradation is disclosed, and the report is NOT refused.
|
|
626
643
|
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]);
|
|
627
|
-
expect(jsonOutput.status).toBe('
|
|
628
|
-
expect(jsonOutput.
|
|
629
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
644
|
+
expect(jsonOutput.status).toBe('succeeded');
|
|
645
|
+
expect(String(jsonOutput.warning ?? '')).toMatch(/empty_trajectory/);
|
|
646
|
+
expect(exitSpy).not.toHaveBeenCalledWith(1);
|
|
630
647
|
|
|
631
648
|
logSpy.mockRestore();
|
|
632
649
|
exitSpy.mockRestore();
|
|
633
650
|
});
|
|
634
651
|
|
|
635
|
-
it('
|
|
652
|
+
it('degrades on evidence_read_failed when the binding was verified (tables unreadable after the sessions check)', async () => {
|
|
653
|
+
// SPEC §8.2 note: a real session with an unreadable trajectory is
|
|
654
|
+
// bound + unavailable — the sessions row was found, only the evidence
|
|
655
|
+
// read failed.
|
|
636
656
|
vi.mocked(acquireTrajectoryEvidenceFromDb).mockReturnValueOnce({
|
|
637
657
|
status: 'unavailable',
|
|
638
658
|
reasonCode: 'evidence_read_failed',
|
|
639
|
-
detail: '
|
|
659
|
+
detail: 'trajectory_tables_unreadable',
|
|
660
|
+
binding: 'verified',
|
|
640
661
|
} as any);
|
|
641
662
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
642
663
|
const exitSpy = mockProcessExit();
|
|
643
664
|
|
|
644
665
|
await handlePainRecord({ reason: 'test pain', session: 'sess-x', json: true });
|
|
645
666
|
|
|
646
|
-
expect(lastRecordPainInput).toBeNull();
|
|
667
|
+
expect(lastRecordPainInput).not.toBeNull();
|
|
668
|
+
expect(lastRecordPainInput!.sessionId).toBe('sess-x');
|
|
669
|
+
expect(lastRecordPainInput!.evidence).toEqual([]);
|
|
670
|
+
expect(lastRecordPainInput!.painIngress.correlation.status).toBe('bound');
|
|
671
|
+
expect(lastRecordPainInput!.painIngress.evidenceClass).toEqual({
|
|
672
|
+
status: 'unavailable',
|
|
673
|
+
reason: 'evidence_read_failed',
|
|
674
|
+
});
|
|
647
675
|
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]);
|
|
648
|
-
expect(jsonOutput.status).toBe('
|
|
649
|
-
expect(jsonOutput.
|
|
650
|
-
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
676
|
+
expect(jsonOutput.status).toBe('succeeded');
|
|
677
|
+
expect(String(jsonOutput.warning ?? '')).toMatch(/evidence_read_failed/);
|
|
678
|
+
expect(exitSpy).not.toHaveBeenCalledWith(1);
|
|
651
679
|
|
|
652
680
|
logSpy.mockRestore();
|
|
653
681
|
exitSpy.mockRestore();
|
|
654
682
|
});
|
|
655
683
|
|
|
656
|
-
it(
|
|
684
|
+
it.each([
|
|
685
|
+
['trajectory_unavailable', 'no trajectory.db at workspace .state'],
|
|
686
|
+
['evidence_read_failed', 'trajectory_db_unreadable: file is not a database'],
|
|
687
|
+
] as const)('refuses %s when the binding is UNVERIFIED (PRI-783 review P1: no verified session, no bound claim)', async (reasonCode, detail) => {
|
|
688
|
+
// PRI-783 review P1: when trajectory.db is missing or unopenable the id
|
|
689
|
+
// could not be verified. Claiming bound here would let the observability
|
|
690
|
+
// writer persist the unverified id into a freshly created trajectory.db —
|
|
691
|
+
// a self-proving loop. It must refuse instead.
|
|
657
692
|
vi.mocked(acquireTrajectoryEvidenceFromDb).mockReturnValueOnce({
|
|
658
693
|
status: 'unavailable',
|
|
659
|
-
reasonCode
|
|
660
|
-
detail
|
|
694
|
+
reasonCode,
|
|
695
|
+
detail,
|
|
696
|
+
binding: 'unverified',
|
|
661
697
|
} as any);
|
|
662
698
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
699
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
663
700
|
const exitSpy = mockProcessExit();
|
|
664
701
|
|
|
665
702
|
await handlePainRecord({ reason: 'test pain', session: 'sess-x', json: true });
|
|
666
703
|
|
|
704
|
+
// Refused before any mutation, with honest next guidance.
|
|
667
705
|
expect(lastRecordPainInput).toBeNull();
|
|
668
706
|
const jsonOutput = JSON.parse(logSpy.mock.calls[0][0]);
|
|
669
707
|
expect(jsonOutput.status).toBe('failed');
|
|
670
|
-
expect(jsonOutput.reason).toBe(
|
|
708
|
+
expect(jsonOutput.reason).toBe(reasonCode);
|
|
709
|
+
expect(jsonOutput.nextAction).not.toMatch(/pd pain record --session/);
|
|
671
710
|
expect(exitSpy).toHaveBeenCalledWith(1);
|
|
672
711
|
|
|
673
712
|
logSpy.mockRestore();
|
|
713
|
+
errorSpy.mockRestore();
|
|
674
714
|
exitSpy.mockRestore();
|
|
675
715
|
});
|
|
676
716
|
|