@principles/pd-cli 1.133.0 → 1.134.0
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/__tests__/intent-flag-wiring.test.js +74 -3
- package/dist/commands/__tests__/intent-flag-wiring.test.js.map +1 -1
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts +2 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.d.ts.map +1 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js +60 -0
- package/dist/commands/__tests__/pain-retry-maxTokens-flag.test.js.map +1 -0
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js +11 -4
- package/dist/commands/__tests__/rulecode-flag-wiring.test.js.map +1 -1
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js +9 -0
- package/dist/commands/__tests__/run-rulehost-flag-wiring.test.js.map +1 -1
- package/dist/commands/admission-gate.d.ts +24 -0
- package/dist/commands/admission-gate.d.ts.map +1 -0
- package/dist/commands/admission-gate.js +29 -0
- package/dist/commands/admission-gate.js.map +1 -0
- package/dist/commands/candidate.d.ts.map +1 -1
- package/dist/commands/candidate.js +72 -6
- package/dist/commands/candidate.js.map +1 -1
- package/dist/commands/console.d.ts.map +1 -1
- package/dist/commands/console.js +40 -0
- package/dist/commands/console.js.map +1 -1
- package/dist/commands/demo-story-a.d.ts.map +1 -1
- package/dist/commands/demo-story-a.js +4 -2
- package/dist/commands/demo-story-a.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +155 -8
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/errors-list.d.ts +25 -0
- package/dist/commands/errors-list.d.ts.map +1 -0
- package/dist/commands/errors-list.js +368 -0
- package/dist/commands/errors-list.js.map +1 -0
- package/dist/commands/health-codex.d.ts +7 -0
- package/dist/commands/health-codex.d.ts.map +1 -0
- package/dist/commands/health-codex.js +209 -0
- package/dist/commands/health-codex.js.map +1 -0
- package/dist/commands/health.d.ts +24 -0
- package/dist/commands/health.d.ts.map +1 -1
- package/dist/commands/health.js +48 -0
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/intent.d.ts +21 -4
- package/dist/commands/intent.d.ts.map +1 -1
- package/dist/commands/intent.js +88 -27
- package/dist/commands/intent.js.map +1 -1
- package/dist/commands/pain-evidence.d.ts +6 -0
- package/dist/commands/pain-evidence.d.ts.map +1 -1
- package/dist/commands/pain-evidence.js +6 -0
- package/dist/commands/pain-evidence.js.map +1 -1
- package/dist/commands/pain-record.d.ts.map +1 -1
- package/dist/commands/pain-record.js +21 -0
- package/dist/commands/pain-record.js.map +1 -1
- package/dist/commands/pain-retry.d.ts +4 -0
- package/dist/commands/pain-retry.d.ts.map +1 -1
- package/dist/commands/pain-retry.js +327 -32
- package/dist/commands/pain-retry.js.map +1 -1
- package/dist/commands/proven-channel-baseline.d.ts.map +1 -1
- package/dist/commands/proven-channel-baseline.js +4 -2
- package/dist/commands/proven-channel-baseline.js.map +1 -1
- package/dist/commands/runtime-activation.d.ts +30 -0
- package/dist/commands/runtime-activation.d.ts.map +1 -1
- package/dist/commands/runtime-activation.js +567 -11
- package/dist/commands/runtime-activation.js.map +1 -1
- package/dist/commands/runtime-canary.d.ts.map +1 -1
- package/dist/commands/runtime-canary.js +32 -7
- package/dist/commands/runtime-canary.js.map +1 -1
- package/dist/commands/runtime-health-snapshot.d.ts.map +1 -1
- package/dist/commands/runtime-health-snapshot.js +4 -2
- package/dist/commands/runtime-health-snapshot.js.map +1 -1
- package/dist/commands/runtime-init.d.ts +43 -0
- package/dist/commands/runtime-init.d.ts.map +1 -0
- package/dist/commands/runtime-init.js +264 -0
- package/dist/commands/runtime-init.js.map +1 -0
- package/dist/commands/runtime-internalization-context-trace.d.ts +25 -0
- package/dist/commands/runtime-internalization-context-trace.d.ts.map +1 -0
- package/dist/commands/runtime-internalization-context-trace.js +374 -0
- package/dist/commands/runtime-internalization-context-trace.js.map +1 -0
- package/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-enqueue-successors.js +3 -1
- package/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -1
- package/dist/commands/runtime-internalization-integrity.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-integrity.js +4 -2
- package/dist/commands/runtime-internalization-integrity.js.map +1 -1
- package/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-once.js +20 -5
- package/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.d.ts +1 -0
- package/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-run-rulehost.js +149 -6
- package/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
- package/dist/commands/runtime-uat.d.ts.map +1 -1
- package/dist/commands/runtime-uat.guard.test.js +13 -15
- package/dist/commands/runtime-uat.guard.test.js.map +1 -1
- package/dist/commands/runtime-uat.js +4 -3
- package/dist/commands/runtime-uat.js.map +1 -1
- package/dist/config-reader.js +1 -1
- package/dist/config-reader.js.map +1 -1
- package/dist/index.js +54 -78
- package/dist/index.js.map +1 -1
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts +2 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.d.ts.map +1 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.js +304 -0
- package/dist/services/__tests__/evaluator-runner-deps.test.js.map +1 -0
- package/dist/services/__tests__/rulehost-readiness.test.js.map +1 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +1 -1
- package/dist/services/config-doctor.d.ts.map +1 -1
- package/dist/services/config-doctor.js +9 -2
- package/dist/services/config-doctor.js.map +1 -1
- package/dist/services/pain-flood-simulation-runner.d.ts.map +1 -1
- package/dist/services/pain-flood-simulation-runner.js +9 -0
- package/dist/services/pain-flood-simulation-runner.js.map +1 -1
- package/dist/services/pd-config-loader.js +2 -2
- package/dist/services/pd-config-loader.js.map +1 -1
- package/dist/services/proven-channel-baseline-runner.d.ts.map +1 -1
- package/dist/services/proven-channel-baseline-runner.js +10 -2
- package/dist/services/proven-channel-baseline-runner.js.map +1 -1
- package/dist/services/quality-scorecard/data-extractor.js.map +1 -1
- package/dist/services/resolve-runtime-from-pd-config.js.map +1 -1
- package/dist/services/rulehost-pipeline-runner.d.ts +77 -1
- package/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/dist/services/rulehost-pipeline-runner.js +111 -7
- package/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/dist/services/rulehost-readiness.js.map +1 -1
- package/dist/services/runtime-adapter-resolver.d.ts +1 -0
- package/dist/services/runtime-adapter-resolver.d.ts.map +1 -1
- package/dist/services/runtime-adapter-resolver.js +3 -0
- package/dist/services/runtime-adapter-resolver.js.map +1 -1
- package/dist/services/synthetic-baseline-runner.d.ts.map +1 -1
- package/dist/services/synthetic-baseline-runner.js +11 -0
- package/dist/services/synthetic-baseline-runner.js.map +1 -1
- package/dist/utils/production-workspace-guard.d.ts +4 -4
- package/dist/utils/production-workspace-guard.d.ts.map +1 -1
- package/dist/utils/production-workspace-guard.js +19 -16
- package/dist/utils/production-workspace-guard.js.map +1 -1
- package/dist/utils/production-workspace-guard.test.js +54 -19
- package/dist/utils/production-workspace-guard.test.js.map +1 -1
- package/package.json +8 -5
- package/src/commands/__tests__/intent-flag-wiring.test.ts +88 -3
- package/src/commands/__tests__/pain-retry-maxTokens-flag.test.ts +73 -0
- package/src/commands/__tests__/rulecode-flag-wiring.test.ts +31 -24
- package/src/commands/__tests__/run-rulehost-flag-wiring.test.ts +12 -0
- package/src/commands/admission-gate.ts +30 -0
- package/src/commands/candidate.ts +74 -6
- package/src/commands/console.ts +40 -0
- package/src/commands/demo-story-a.ts +4 -2
- package/src/commands/diagnose.ts +158 -10
- package/src/commands/errors-list.ts +440 -0
- package/src/commands/health-codex.ts +249 -0
- package/src/commands/health.ts +60 -0
- package/src/commands/intent.ts +98 -29
- package/src/commands/pain-evidence.ts +6 -0
- package/src/commands/pain-record.ts +21 -0
- package/src/commands/pain-retry.ts +353 -35
- package/src/commands/proven-channel-baseline.ts +4 -2
- package/src/commands/runtime-activation.ts +657 -12
- package/src/commands/runtime-canary.ts +31 -5
- package/src/commands/runtime-health-snapshot.ts +4 -2
- package/src/commands/runtime-init.ts +305 -0
- package/src/commands/runtime-internalization-context-trace.ts +445 -0
- package/src/commands/runtime-internalization-enqueue-successors.ts +3 -1
- package/src/commands/runtime-internalization-integrity.ts +4 -2
- package/src/commands/runtime-internalization-run-once.ts +20 -5
- package/src/commands/runtime-internalization-run-rulehost.ts +159 -6
- package/src/commands/runtime-uat.guard.test.ts +14 -16
- package/src/commands/runtime-uat.ts +4 -3
- package/src/config-reader.ts +1 -1
- package/src/index.ts +69 -78
- package/src/services/__tests__/evaluator-runner-deps.test.ts +341 -0
- package/src/services/config-doctor.ts +9 -2
- package/src/services/pain-flood-simulation-runner.ts +9 -0
- package/src/services/pd-config-loader.ts +2 -2
- package/src/services/proven-channel-baseline-runner.ts +10 -3
- package/src/services/rulehost-pipeline-runner.ts +157 -6
- package/src/services/runtime-adapter-resolver.ts +4 -0
- package/src/services/synthetic-baseline-runner.ts +11 -0
- package/src/utils/production-workspace-guard.test.ts +62 -20
- package/src/utils/production-workspace-guard.ts +19 -16
- package/tests/bdd/cli-contract.steps.ts +343 -0
- package/tests/bdd/context-trace.steps.ts +229 -0
- package/tests/bdd/support/gherkin-loader.ts +147 -0
- package/tests/bdd/support/repo-root.ts +62 -0
- package/tests/bdd/support/vitest-bdd.ts +179 -0
- package/tests/commands/admission-gate-coverage.test.ts +79 -0
- package/tests/commands/candidate-audit-repair.test.ts +77 -0
- package/tests/commands/candidate-intake.test.ts +155 -10
- package/tests/commands/candidate-internalization-backfill.test.ts +30 -0
- package/tests/commands/cli-help-snapshot.test.ts +15 -1
- package/tests/commands/console-open.test.ts +19 -0
- package/tests/commands/diagnose.test.ts +619 -14
- package/tests/commands/errors-list.test.ts +678 -0
- package/tests/commands/health-codex.test.ts +243 -0
- package/tests/commands/health-host-wiring.test.ts +76 -0
- package/tests/commands/intent.test.ts +4 -4
- package/tests/commands/pain-record.test.ts +80 -0
- package/tests/commands/pain-retry.test.ts +94 -3
- package/tests/commands/run-rulehost-handler.test.ts +133 -0
- package/tests/commands/runtime-activation-approve-flag-wiring.test.ts +227 -0
- package/tests/commands/runtime-activation-deactivate-flag-wiring.test.ts +167 -0
- package/tests/commands/runtime-activation-dispatch-flag-wiring.test.ts +262 -0
- package/tests/commands/runtime-activation-list-flag-wiring.test.ts +193 -0
- package/tests/commands/runtime-activation-promote-flag-wiring.test.ts +222 -0
- package/tests/commands/runtime-activation.test.ts +1090 -5
- package/tests/commands/runtime-canary.test.ts +50 -0
- package/tests/commands/runtime-features.test.ts +2 -0
- package/tests/commands/runtime-init-empty-workspace.test.ts +234 -0
- package/tests/commands/runtime-init.test.ts +346 -0
- package/tests/commands/runtime-internalization-context-trace.test.ts +110 -0
- package/tests/commands/runtime-internalization.test.ts +2 -2
- package/tests/config-reader.test.ts +6 -5
- package/tests/e2e/cross-package-acceptance.test.ts +55 -13
- package/tests/services/mainline-snapshot-assembler.test.ts +6 -3
- package/tests/services/resolve-runtime-from-pd-config.test.ts +2 -0
- package/tests/services/rulehost-pipeline-runner.test.ts +1 -1
- package/vitest.config.ts +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command-registration / parser tests for context-trace (PR 5 task 11.2, cli-7).
|
|
3
|
+
*
|
|
4
|
+
* Tests the real Commander program — exercises actual flag parsing, not just
|
|
5
|
+
* the handler. Verifies:
|
|
6
|
+
* - --task / --artifact / --workspace / --json parse correctly
|
|
7
|
+
* - --dry-run / --confirm do NOT exist (read-only command, cli-4)
|
|
8
|
+
* - no new feature flag is introduced (registry diff is empty)
|
|
9
|
+
*
|
|
10
|
+
* @see .kiro/specs/internalization-progressive-disclosure/design.md §6.7
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, it, expect } from 'vitest';
|
|
14
|
+
import { Command } from 'commander';
|
|
15
|
+
import { DEFAULT_FEATURE_FLAGS } from '@principles/core/runtime-v2';
|
|
16
|
+
|
|
17
|
+
// Build a minimal program that mirrors the real registration.
|
|
18
|
+
function buildTestProgram(): Command {
|
|
19
|
+
const program = new Command();
|
|
20
|
+
const runtime = program.command('runtime');
|
|
21
|
+
const internalization = runtime.command('internalization', { hidden: true });
|
|
22
|
+
|
|
23
|
+
internalization
|
|
24
|
+
.command('context-trace')
|
|
25
|
+
.description('Trace the internalization context chain')
|
|
26
|
+
.requiredOption('-t, --task <taskId>', 'Task ID to trace')
|
|
27
|
+
.option('-a, --artifact <artifactId>', 'Specific artifact ID to start from')
|
|
28
|
+
.option('-w, --workspace <path>', 'Workspace directory')
|
|
29
|
+
.option('--json', 'Output raw JSON (default)')
|
|
30
|
+
.action(() => {});
|
|
31
|
+
|
|
32
|
+
return program;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe('context-trace command registration (cli-7)', () => {
|
|
36
|
+
it('parses --task correctly', () => {
|
|
37
|
+
const program = buildTestProgram();
|
|
38
|
+
program.parse(['node', 'pd', 'runtime', 'internalization', 'context-trace', '--task', 'task-123']);
|
|
39
|
+
const cmd = program.commands[0]?.commands[0]?.commands[0];
|
|
40
|
+
expect(cmd).toBeDefined();
|
|
41
|
+
expect(cmd?.opts().task).toBe('task-123');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('parses --artifact correctly', () => {
|
|
45
|
+
const program = buildTestProgram();
|
|
46
|
+
program.parse(['node', 'pd', 'runtime', 'internalization', 'context-trace', '--task', 't1', '--artifact', 'art-1']);
|
|
47
|
+
const cmd = program.commands[0]?.commands[0]?.commands[0];
|
|
48
|
+
expect(cmd?.opts().artifact).toBe('art-1');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('parses --workspace correctly', () => {
|
|
52
|
+
const program = buildTestProgram();
|
|
53
|
+
program.parse(['node', 'pd', 'runtime', 'internalization', 'context-trace', '--task', 't1', '--workspace', '/tmp/pd']);
|
|
54
|
+
const cmd = program.commands[0]?.commands[0]?.commands[0];
|
|
55
|
+
expect(cmd?.opts().workspace).toBe('/tmp/pd');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('parses --json flag', () => {
|
|
59
|
+
const program = buildTestProgram();
|
|
60
|
+
program.parse(['node', 'pd', 'runtime', 'internalization', 'context-trace', '--task', 't1', '--json']);
|
|
61
|
+
const cmd = program.commands[0]?.commands[0]?.commands[0];
|
|
62
|
+
expect(cmd?.opts().json).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('requires --task (exits with error if missing)', () => {
|
|
66
|
+
const program = buildTestProgram();
|
|
67
|
+
// Commander calls process.exit on missing required option; stub it.
|
|
68
|
+
const originalExit = process.exit;
|
|
69
|
+
let exitCode: number | undefined;
|
|
70
|
+
process.exit = ((code?: number) => { exitCode = code; }) as never;
|
|
71
|
+
try {
|
|
72
|
+
program.parse(['node', 'pd', 'runtime', 'internalization', 'context-trace']);
|
|
73
|
+
} catch {
|
|
74
|
+
// Commander may throw after stubbed exit
|
|
75
|
+
} finally {
|
|
76
|
+
process.exit = originalExit;
|
|
77
|
+
}
|
|
78
|
+
expect(exitCode).toBe(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('does NOT have --dry-run option (read-only command, cli-4)', () => {
|
|
82
|
+
const program = buildTestProgram();
|
|
83
|
+
program.parse(['node', 'pd', 'runtime', 'internalization', 'context-trace', '--task', 't1']);
|
|
84
|
+
const cmd = program.commands[0]?.commands[0]?.commands[0];
|
|
85
|
+
expect(cmd?.opts().dryRun).toBeUndefined();
|
|
86
|
+
// Verify the option doesn't exist at all.
|
|
87
|
+
const dryRunOption = cmd?.options.find((o) => o.long === '--dry-run');
|
|
88
|
+
expect(dryRunOption).toBeUndefined();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('does NOT have --confirm option (read-only command, cli-4)', () => {
|
|
92
|
+
const program = buildTestProgram();
|
|
93
|
+
program.parse(['node', 'pd', 'runtime', 'internalization', 'context-trace', '--task', 't1']);
|
|
94
|
+
const cmd = program.commands[0]?.commands[0]?.commands[0];
|
|
95
|
+
expect(cmd?.opts().confirm).toBeUndefined();
|
|
96
|
+
const confirmOption = cmd?.options.find((o) => o.long === '--confirm');
|
|
97
|
+
expect(confirmOption).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('context-trace does not introduce a new feature flag (registry diff empty)', () => {
|
|
101
|
+
// design §8: Layer 3 (CLI) does not add a new flag. The command is always
|
|
102
|
+
// available (read-only); it reads the EXISTING 3 flags.
|
|
103
|
+
const knownFlags = new Set(DEFAULT_FEATURE_FLAGS.map((f) => f.id));
|
|
104
|
+
expect(knownFlags.has('artifact_summary_redundancy')).toBe(true);
|
|
105
|
+
expect(knownFlags.has('context_manifest_budget')).toBe(true);
|
|
106
|
+
expect(knownFlags.has('progressive_evaluator')).toBe(true);
|
|
107
|
+
// No 'context_trace' flag should exist.
|
|
108
|
+
expect(knownFlags.has('context_trace')).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -46,8 +46,8 @@ describe('CLI command tree: pd runtime internalization', () => {
|
|
|
46
46
|
expect(output).toContain('--json');
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it('runtime subcommand list
|
|
49
|
+
it('runtime subcommand list includes internalization (PRI-419 amendment: un-hidden for operator visibility)', () => {
|
|
50
50
|
const output = runPdHelp(['runtime', '--help']);
|
|
51
|
-
expect(output).
|
|
51
|
+
expect(output).toMatch(/internalization\s/);
|
|
52
52
|
});
|
|
53
53
|
});
|
|
@@ -22,15 +22,16 @@ vi.mock('fs', () => ({
|
|
|
22
22
|
readFileSync: vi.fn(),
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
+
// Mock js-yaml with NAMED exports (matches `import * as yaml from 'js-yaml'`
|
|
26
|
+
// in config-reader.ts). js-yaml 5.x removed the default export, so the mock
|
|
27
|
+
// must provide `load` and `JSON_SCHEMA` as named members, not under `default`.
|
|
25
28
|
vi.mock('js-yaml', () => ({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
JSON_SCHEMA: 'JSON_SCHEMA',
|
|
29
|
-
},
|
|
29
|
+
load: vi.fn(),
|
|
30
|
+
JSON_SCHEMA: 'JSON_SCHEMA',
|
|
30
31
|
}));
|
|
31
32
|
|
|
32
33
|
import * as fs from 'fs';
|
|
33
|
-
import yaml from 'js-yaml';
|
|
34
|
+
import * as yaml from 'js-yaml';
|
|
34
35
|
|
|
35
36
|
const mockExistsSync = vi.mocked(fs.existsSync);
|
|
36
37
|
const mockReadFileSync = vi.mocked(fs.readFileSync);
|
|
@@ -173,7 +173,7 @@ function artificerV2(taskId: string, priorId?: string): unknown {
|
|
|
173
173
|
return {
|
|
174
174
|
taskId, sourceScribeArtifactId: requireLineage(priorId, 'sourceScribeArtifactId'),
|
|
175
175
|
implementationPlan: { summary: 'Block /etc writes', targetSurface: 'rule-host', changes: ['matcher'], tests: ['unit'], rolloutNotes: ['shadow'], confidence: 0.85 },
|
|
176
|
-
implementationCode: 'function evaluate(input, helpers) { const p = String(input?.action?.paramsSummary?.path ?? input?.action?.normalizedPath ?? "");
|
|
176
|
+
implementationCode: 'function evaluate(input, helpers) { const p = String(input?.action?.paramsSummary?.path ?? input?.action?.normalizedPath ?? ""); if (p.startsWith("/etc")) return { decision: "block", matched: true, reason: "system path" }; const ctx = input?.context; if (ctx && ctx.facts && ctx.facts.priorReadOfTarget === "no") { return { decision: "block", matched: true, reason: "no prior read" }; } return { decision: "allow", matched: false, reason: "ok" }; }',
|
|
177
177
|
implementationSummary: 'Block system path writes',
|
|
178
178
|
goldenTraceCases: [
|
|
179
179
|
{ caseId: 'pos-1', kind: 'positive', toolName: 'write_file', params: { path: '/project/f.txt' }, expectedDecision: 'allow' },
|
|
@@ -397,21 +397,23 @@ describe('Cross-Package Acceptance Test (PRI-408 P1/P2 fixes) — unsplippable c
|
|
|
397
397
|
expect(activationRecord!.artifactId).toBe(revisedArtifactId);
|
|
398
398
|
expect(activationRecord!.channel).toBe('code_tool_hook');
|
|
399
399
|
expect(activationRecord!.deactivatedAt).toBeNull();
|
|
400
|
+
// PRI-489: Owner approval creates a SHADOW activation first (not live).
|
|
401
|
+
// Shadow activations are observation-only — they record would-block into
|
|
402
|
+
// shadowDecisions but never actually block the tool call. The only
|
|
403
|
+
// shadow -> live transition is `pd activation promote --confirm`.
|
|
404
|
+
expect(activationRecord!.action).toBe('code_tool_hook_shadow_activate');
|
|
400
405
|
|
|
401
406
|
// Verify via listCodeToolHookActivations (P2 #5: default excludes deactivated)
|
|
402
407
|
const activeActivations = await stateStore.listCodeToolHookActivations();
|
|
403
408
|
const ourActivation = activeActivations.find(a => a.artifactId === revisedArtifactId);
|
|
404
409
|
expect(ourActivation).toBeDefined();
|
|
405
410
|
expect(ourActivation!.deactivatedAt).toBeNull();
|
|
411
|
+
expect(ourActivation!.action).toBe('code_tool_hook_shadow_activate');
|
|
406
412
|
|
|
407
|
-
// ── Step
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
// 2. Compile the rule code in the production vm sandbox
|
|
412
|
-
// 3. Call evaluate() with system-path and normal-path inputs
|
|
413
|
-
// 4. Verify the rule blocks system paths and allows normal paths
|
|
414
|
-
|
|
413
|
+
// ── Step 7a: Verify shadow activation does NOT block (PRI-489) ────────
|
|
414
|
+
// Shadow mode is observation-only. The RuleHost loads the activation but
|
|
415
|
+
// records shadowDecisions instead of blocking. evaluate() must return
|
|
416
|
+
// undefined (no block) for shadow activations, even for /etc/passwd.
|
|
415
417
|
const makeRuleHostInput = (targetPath: string): RuleHostInput => ({
|
|
416
418
|
action: { toolName: 'write_file', normalizedPath: targetPath, paramsSummary: { path: targetPath } },
|
|
417
419
|
workspace: { isRiskPath: targetPath.startsWith('/etc'), planStatus: 'NONE', hasPlanFile: false },
|
|
@@ -419,6 +421,43 @@ describe('Cross-Package Acceptance Test (PRI-408 P1/P2 fixes) — unsplippable c
|
|
|
419
421
|
evolution: { epTier: 0 },
|
|
420
422
|
derived: { estimatedLineChanges: 1, bashRisk: 'safe' },
|
|
421
423
|
});
|
|
424
|
+
const shadowRuleHost = new RuleHost(
|
|
425
|
+
path.join(tmpDir, '.state'),
|
|
426
|
+
{ warn: () => {} },
|
|
427
|
+
{ workspaceDir: tmpDir },
|
|
428
|
+
);
|
|
429
|
+
try {
|
|
430
|
+
const report = shadowRuleHost.evaluateDetailed(makeRuleHostInput('/etc/passwd'));
|
|
431
|
+
expect(report.liveDecision).toBeUndefined();
|
|
432
|
+
expect(report.shadowDecisions).toContainEqual(
|
|
433
|
+
expect.objectContaining({ activationId: activationRecord!.activationId }),
|
|
434
|
+
);
|
|
435
|
+
} finally {
|
|
436
|
+
shadowRuleHost.dispose();
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ── Step 7b: Promote shadow → live (PRI-489) ──────────────────────────
|
|
440
|
+
// `pd activation promote --activation-id ... --confirm` is the ONLY
|
|
441
|
+
// shadow → live entry. SqliteActivationStateStore.promoteActivation
|
|
442
|
+
// atomically rewrites action to `code_tool_hook_live_activate` inside a
|
|
443
|
+
// BEGIN IMMEDIATE transaction.
|
|
444
|
+
const activationId = activationRecord!.activationId;
|
|
445
|
+
const promoteResult = await stateStore.promoteActivation(activationId, new Date().toISOString());
|
|
446
|
+
expect(promoteResult).toBe(true);
|
|
447
|
+
|
|
448
|
+
// Verify the action is now live
|
|
449
|
+
const liveActivationRecord = await stateStore.getActivationStatus(idempotencyKey);
|
|
450
|
+
expect(liveActivationRecord).not.toBeNull();
|
|
451
|
+
expect(liveActivationRecord!.action).toBe('code_tool_hook_live_activate');
|
|
452
|
+
expect(liveActivationRecord!.promotedAt).not.toBeNull();
|
|
453
|
+
|
|
454
|
+
// ── Step 7c: Verify the rule actually blocks in live mode (P1 #4) ────
|
|
455
|
+
// The production RuleHost gate loads all active rules, compiles their code,
|
|
456
|
+
// and calls evaluate() on each tool call. This step simulates that:
|
|
457
|
+
// 1. Load active rules from the activation state store
|
|
458
|
+
// 2. Compile the rule code in the production vm sandbox
|
|
459
|
+
// 3. Call evaluate() with system-path and normal-path inputs
|
|
460
|
+
// 4. Verify the rule blocks system paths and allows normal paths
|
|
422
461
|
const ruleHost = new RuleHost(
|
|
423
462
|
path.join(tmpDir, '.state'),
|
|
424
463
|
{ warn: () => {} },
|
|
@@ -431,7 +470,6 @@ describe('Cross-Package Acceptance Test (PRI-408 P1/P2 fixes) — unsplippable c
|
|
|
431
470
|
expect(ruleHost.evaluate(makeRuleHostInput('/project/src/main.ts'))).toBeUndefined();
|
|
432
471
|
|
|
433
472
|
// ── Step 8: Owner deactivates (rollback) ───────────────────────────────
|
|
434
|
-
const activationId = activationRecord!.activationId;
|
|
435
473
|
const deactivateResult = await stateStore.deactivateActivation(activationId, new Date().toISOString());
|
|
436
474
|
expect(deactivateResult).toBe(true);
|
|
437
475
|
|
|
@@ -461,10 +499,14 @@ describe('Cross-Package Acceptance Test (PRI-408 P1/P2 fixes) — unsplippable c
|
|
|
461
499
|
expect(deactivatedRecord).toBeDefined();
|
|
462
500
|
expect(deactivatedRecord!.deactivatedAt).not.toBeNull();
|
|
463
501
|
|
|
464
|
-
// ── Step 10: Verify
|
|
502
|
+
// ── Step 10: Verify getActivationStatus reflects deactivation (Bug-Q) ─
|
|
503
|
+
// Bug-Q fix: getActivationStatus now filters out deactivated records
|
|
504
|
+
// (WHERE deactivated_at IS NULL), returning null when no active activation
|
|
505
|
+
// exists. This allows re-activation after deactivation. The deactivated
|
|
506
|
+
// record itself is already verified via listCodeToolHookActivations(true)
|
|
507
|
+
// above (L459-462, Step 9). Here we verify the new null semantics.
|
|
465
508
|
const activationAfterDeactivate = await stateStore.getActivationStatus(idempotencyKey);
|
|
466
|
-
expect(activationAfterDeactivate).
|
|
467
|
-
expect(activationAfterDeactivate!.deactivatedAt).not.toBeNull();
|
|
509
|
+
expect(activationAfterDeactivate).toBeNull();
|
|
468
510
|
|
|
469
511
|
await sm2.close();
|
|
470
512
|
}, 60_000);
|
|
@@ -401,11 +401,14 @@ describe('assembleMainlineSnapshot', () => {
|
|
|
401
401
|
await sm.updateCandidateStatus(candidate!.candidateId, { status: 'consumed' });
|
|
402
402
|
|
|
403
403
|
// Insert a row with empty candidate_id to exercise the malformed-row path.
|
|
404
|
+
// F13 (PRI-442): include consumed_at so the row satisfies the schema
|
|
405
|
+
// CHECK constraint (status='consumed' → consumed_at IS NOT NULL). The
|
|
406
|
+
// malformation under test is the empty candidate_id, not the timestamp.
|
|
404
407
|
const db = sm.connection.getDb();
|
|
405
408
|
db.prepare(
|
|
406
|
-
`INSERT INTO principle_candidates (candidate_id, task_id, artifact_id, source_run_id, title, description, idempotency_key, status, created_at, recommendation_kind)
|
|
407
|
-
VALUES ('', ?, ?, ?, 'malformed', '', ?, 'consumed', ?, 'principle')`,
|
|
408
|
-
).run(taskId, candidate!.artifactId, candidate!.sourceRunId, `idemp-${Date.now()}`, new Date().toISOString());
|
|
409
|
+
`INSERT INTO principle_candidates (candidate_id, task_id, artifact_id, source_run_id, title, description, idempotency_key, status, created_at, consumed_at, recommendation_kind)
|
|
410
|
+
VALUES ('', ?, ?, ?, 'malformed', '', ?, 'consumed', ?, ?, 'principle')`,
|
|
411
|
+
).run(taskId, candidate!.artifactId, candidate!.sourceRunId, `idemp-${Date.now()}`, new Date().toISOString(), new Date().toISOString());
|
|
409
412
|
|
|
410
413
|
const { snapshot, warnings } = await assembleMainlineSnapshot({ workspaceDir, painId, readiness: healthyReadiness() });
|
|
411
414
|
|
|
@@ -266,6 +266,8 @@ describe('resolveRuntimeFromPdConfig', () => {
|
|
|
266
266
|
const tmp = mkTmpDir();
|
|
267
267
|
try {
|
|
268
268
|
const result = resolveRuntimeFromPdConfig(tmp);
|
|
269
|
+
// Default profile is openclaw.default (MVP default runtime) → resolves
|
|
270
|
+
// successfully without env var.
|
|
269
271
|
expect(isRuntimeConfigError(result.result)).toBe(false);
|
|
270
272
|
expect(result.configSource).toBe('.pd/config.yaml');
|
|
271
273
|
expect(result.runtimeProfileId).toBe('openclaw.default');
|
|
@@ -137,7 +137,7 @@ function artificerV2(taskId: string, priorId?: string): unknown {
|
|
|
137
137
|
return {
|
|
138
138
|
taskId, sourceScribeArtifactId: requireLineage(priorId, 'sourceScribeArtifactId'),
|
|
139
139
|
implementationPlan: { summary: 'Block /etc writes', targetSurface: 'rule-host', changes: ['matcher'], tests: ['unit'], rolloutNotes: ['shadow'], confidence: 0.85 },
|
|
140
|
-
implementationCode: 'function evaluate(input, helpers) { const p = String(input?.action?.paramsSummary?.path ?? input?.action?.normalizedPath ?? "");
|
|
140
|
+
implementationCode: 'function evaluate(input, helpers) { const p = String(input?.action?.paramsSummary?.path ?? input?.action?.normalizedPath ?? ""); if (p.startsWith("/etc")) return { decision: "block", matched: true, reason: "system path" }; const ctx = input?.context; if (ctx && ctx.facts && ctx.facts.priorReadOfTarget === "no") { return { decision: "block", matched: true, reason: "no prior read" }; } return { decision: "allow", matched: false, reason: "ok" }; }',
|
|
141
141
|
implementationSummary: 'Block system path writes',
|
|
142
142
|
goldenTraceCases: [
|
|
143
143
|
{ caseId: 'pos-1', kind: 'positive', toolName: 'write_file', params: { path: '/project/f.txt' }, expectedDecision: 'allow' },
|
package/vitest.config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config';
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
test: {
|
|
5
5
|
environment: 'node',
|
|
6
|
-
include: ['tests/**/*.test.ts', 'src/**/*.test.ts'],
|
|
6
|
+
include: ['tests/bdd/**/*.{test,steps}.ts', 'tests/**/*.test.ts', 'src/**/*.test.ts'],
|
|
7
7
|
exclude: ['**/node_modules/**', '**/dist/**'],
|
|
8
8
|
},
|
|
9
9
|
});
|