@principles/core 1.263.0 → 1.264.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/runtime-v2/__tests__/architecture-regression.test.js +8 -2
- package/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.d.ts +2 -0
- package/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.d.ts.map +1 -0
- package/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.js +91 -0
- package/dist/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.js.map +1 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.d.ts +2 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.d.ts.map +1 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js +83 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.js.map +1 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.d.ts +2 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.d.ts.map +1 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.js +48 -0
- package/dist/runtime-v2/activation/__tests__/promotion-readiness-reader.test.js.map +1 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.d.ts +2 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.d.ts.map +1 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js +141 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.js.map +1 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.d.ts +2 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.d.ts.map +1 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.js +31 -0
- package/dist/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.js.map +1 -0
- package/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.d.ts +2 -0
- package/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.d.ts.map +1 -0
- package/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js +311 -0
- package/dist/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.js.map +1 -0
- package/dist/runtime-v2/activation/activation-control-types.d.ts +61 -0
- package/dist/runtime-v2/activation/activation-control-types.d.ts.map +1 -0
- package/dist/runtime-v2/activation/activation-control-types.js +2 -0
- package/dist/runtime-v2/activation/activation-control-types.js.map +1 -0
- package/dist/runtime-v2/activation/index.d.ts +14 -0
- package/dist/runtime-v2/activation/index.d.ts.map +1 -1
- package/dist/runtime-v2/activation/index.js +7 -0
- package/dist/runtime-v2/activation/index.js.map +1 -1
- package/dist/runtime-v2/activation/openclaw-promotion-checks.d.ts +30 -0
- package/dist/runtime-v2/activation/openclaw-promotion-checks.d.ts.map +1 -0
- package/dist/runtime-v2/activation/openclaw-promotion-checks.js +124 -0
- package/dist/runtime-v2/activation/openclaw-promotion-checks.js.map +1 -0
- package/dist/runtime-v2/activation/promotion-readiness-evaluator.d.ts +17 -0
- package/dist/runtime-v2/activation/promotion-readiness-evaluator.d.ts.map +1 -0
- package/dist/runtime-v2/activation/promotion-readiness-evaluator.js +55 -0
- package/dist/runtime-v2/activation/promotion-readiness-evaluator.js.map +1 -0
- package/dist/runtime-v2/activation/promotion-readiness-reader.d.ts +21 -0
- package/dist/runtime-v2/activation/promotion-readiness-reader.d.ts.map +1 -0
- package/dist/runtime-v2/activation/promotion-readiness-reader.js +44 -0
- package/dist/runtime-v2/activation/promotion-readiness-reader.js.map +1 -0
- package/dist/runtime-v2/activation/rulecode-owner-decision-service.d.ts +104 -0
- package/dist/runtime-v2/activation/rulecode-owner-decision-service.d.ts.map +1 -0
- package/dist/runtime-v2/activation/rulecode-owner-decision-service.js +86 -0
- package/dist/runtime-v2/activation/rulecode-owner-decision-service.js.map +1 -0
- package/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.d.ts +25 -0
- package/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.d.ts.map +1 -0
- package/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.js +23 -0
- package/dist/runtime-v2/activation/rulecode-safety-circuit-breaker.js.map +1 -0
- package/dist/runtime-v2/activation/rulecode-shadow-summary.d.ts +14 -0
- package/dist/runtime-v2/activation/rulecode-shadow-summary.d.ts.map +1 -0
- package/dist/runtime-v2/activation/rulecode-shadow-summary.js +57 -0
- package/dist/runtime-v2/activation/rulecode-shadow-summary.js.map +1 -0
- package/dist/runtime-v2/activation/sqlite-activation-safety-store.d.ts +40 -0
- package/dist/runtime-v2/activation/sqlite-activation-safety-store.d.ts.map +1 -0
- package/dist/runtime-v2/activation/sqlite-activation-safety-store.js +461 -0
- package/dist/runtime-v2/activation/sqlite-activation-safety-store.js.map +1 -0
- package/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts.map +1 -1
- package/dist/runtime-v2/activation/sqlite-activation-state-store.js +24 -6
- package/dist/runtime-v2/activation/sqlite-activation-state-store.js.map +1 -1
- package/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js +19 -4
- package/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js.map +1 -1
- package/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
- package/dist/runtime-v2/activation/writers/rule-host-writer.js +10 -0
- package/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
- package/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +13 -0
- package/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -1
- package/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +15 -2
- package/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
- package/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
- package/dist/runtime-v2/feature-flags/feature-flag-contract.js +2 -0
- package/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
- package/dist/runtime-v2/index.d.ts +2 -2
- package/dist/runtime-v2/index.d.ts.map +1 -1
- package/dist/runtime-v2/index.js +1 -1
- package/dist/runtime-v2/index.js.map +1 -1
- package/dist/runtime-v2/internalization/__tests__/legacy-rule-contract-scanner.test.js +20 -1
- package/dist/runtime-v2/internalization/__tests__/legacy-rule-contract-scanner.test.js.map +1 -1
- package/dist/runtime-v2/internalization/legacy-rule-contract-scanner.d.ts +4 -4
- package/dist/runtime-v2/internalization/legacy-rule-contract-scanner.d.ts.map +1 -1
- package/dist/runtime-v2/internalization/legacy-rule-contract-scanner.js +92 -5
- package/dist/runtime-v2/internalization/legacy-rule-contract-scanner.js.map +1 -1
- package/dist/runtime-v2/store/sqlite-connection-schema-version.test.js +11 -11
- package/dist/runtime-v2/store/sqlite-connection-schema-version.test.js.map +1 -1
- package/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/dist/runtime-v2/store/sqlite-connection.js +111 -0
- package/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-promotion-checks.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createProductionGateDeps } from '../production-gate-deps.js';
|
|
3
|
+
import { RuleHostWriter } from '../writers/rule-host-writer.js';
|
|
4
|
+
import { collectOpenClawPromotionChecks } from '../openclaw-promotion-checks.js';
|
|
5
|
+
const OPENCLAW_HOST_LIVENESS_CONTRACT = {
|
|
6
|
+
version: 'openclaw-legacy@1',
|
|
7
|
+
supportsShadowEvidence: true,
|
|
8
|
+
outOfBandControls: ['activation_deactivate', 'global_rulecode_pause', 'owner_review_console'],
|
|
9
|
+
protectedCapabilities: [
|
|
10
|
+
{ capabilityId: 'pd_status', hostToolAliases: ['bash'] },
|
|
11
|
+
{ capabilityId: 'rulecode_deactivate', hostToolAliases: ['bash'] },
|
|
12
|
+
{ capabilityId: 'rulecode_global_pause', hostToolAliases: ['bash'] },
|
|
13
|
+
{ capabilityId: 'owner_review_access', hostToolAliases: ['owner_review_access'] },
|
|
14
|
+
],
|
|
15
|
+
neutralProbes: [
|
|
16
|
+
{ probeId: 'status', capabilityId: 'pd_status', toolName: 'bash', params: { command: 'pd status' }, expectedDecision: 'allow' },
|
|
17
|
+
{ probeId: 'deactivate', capabilityId: 'rulecode_deactivate', toolName: 'bash', params: { command: 'pd activation deactivate --activation-id test' }, expectedDecision: 'allow' },
|
|
18
|
+
{ probeId: 'pause', capabilityId: 'rulecode_global_pause', toolName: 'bash', params: { command: 'pd activation emergency-pause' }, expectedDecision: 'allow' },
|
|
19
|
+
{ probeId: 'review', capabilityId: 'owner_review_access', toolName: 'owner_review_access', params: {}, expectedDecision: 'allow' },
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
function artifact(id, implementationCode) {
|
|
23
|
+
return {
|
|
24
|
+
artifactId: id,
|
|
25
|
+
artifactKind: 'rule',
|
|
26
|
+
sourceTaskId: `task-${id}`,
|
|
27
|
+
sourcePrincipleId: 'principle-1',
|
|
28
|
+
sourceRuleId: `rule-${id}`,
|
|
29
|
+
lineageArtifactIds: ['parent-1'],
|
|
30
|
+
validationStatus: 'validated',
|
|
31
|
+
contentJson: JSON.stringify({
|
|
32
|
+
implementationCode,
|
|
33
|
+
goldenTrace: {
|
|
34
|
+
traceId: `trace-${id}`,
|
|
35
|
+
sourcePainId: 'pain-1',
|
|
36
|
+
cases: [
|
|
37
|
+
{ caseId: 'negative', kind: 'negative', toolName: 'edit_file', params: { path: '/etc/passwd' }, expectedDecision: 'block' },
|
|
38
|
+
{ caseId: 'positive', kind: 'positive', toolName: 'edit_file', params: { path: '/workspace/safe.ts' }, expectedDecision: 'allow' },
|
|
39
|
+
],
|
|
40
|
+
createdAt: '2026-08-21T00:00:00.000Z',
|
|
41
|
+
version: 1,
|
|
42
|
+
},
|
|
43
|
+
ruleHostGateDecision: 'accepted_shadow',
|
|
44
|
+
affectedTools: ['edit_file', 'bash'],
|
|
45
|
+
}),
|
|
46
|
+
createdAt: '2026-08-21T00:00:00.000Z',
|
|
47
|
+
updatedAt: '2026-08-21T00:00:00.000Z',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const safeCode = `function evaluate(input) {
|
|
51
|
+
const risky = input.action.toolName === 'edit_file' && input.action.paramsSummary.path === '/etc/passwd';
|
|
52
|
+
return { decision: risky ? 'block' : 'allow', matched: risky, reason: risky ? 'risk path' : 'neutral' };
|
|
53
|
+
}`;
|
|
54
|
+
const controlBlockingCode = `function evaluate(input) {
|
|
55
|
+
const blocksControl = input.action.toolName === 'bash';
|
|
56
|
+
const risky = input.action.toolName === 'edit_file' && input.action.paramsSummary.path === '/etc/passwd';
|
|
57
|
+
return { decision: blocksControl || risky ? 'block' : 'allow', matched: blocksControl || risky, reason: blocksControl ? 'deny shell' : risky ? 'risk path' : 'neutral' };
|
|
58
|
+
}`;
|
|
59
|
+
describe('OpenClaw Host Liveness promotion checks', () => {
|
|
60
|
+
const writer = new RuleHostWriter({ gateDeps: createProductionGateDeps() });
|
|
61
|
+
it('runs neutral probes through production validation for the candidate and live composition', async () => {
|
|
62
|
+
const checks = await collectOpenClawPromotionChecks(artifact('candidate', safeCode), {
|
|
63
|
+
ownerIdentityConfigured: true,
|
|
64
|
+
safetyControlsEnabled: true,
|
|
65
|
+
hostContract: OPENCLAW_HOST_LIVENESS_CONTRACT,
|
|
66
|
+
existingLiveArtifacts: [artifact('live', controlBlockingCode)],
|
|
67
|
+
validateProductionArtifact: value => writer.canActivate(value),
|
|
68
|
+
});
|
|
69
|
+
expect(checks.find(check => check.checkId === 'host_liveness_composition')).toEqual({
|
|
70
|
+
checkId: 'host_liveness_composition',
|
|
71
|
+
status: 'failed',
|
|
72
|
+
reasonCode: 'neutral_probe_or_live_composition_failed',
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it('fails compatibility, emergency, shadow evidence, and composition when the adapter contract is unavailable', async () => {
|
|
76
|
+
const checks = await collectOpenClawPromotionChecks(artifact('candidate', safeCode), {
|
|
77
|
+
ownerIdentityConfigured: true,
|
|
78
|
+
safetyControlsEnabled: true,
|
|
79
|
+
hostContract: null,
|
|
80
|
+
existingLiveArtifacts: [],
|
|
81
|
+
validateProductionArtifact: value => writer.canActivate(value),
|
|
82
|
+
});
|
|
83
|
+
expect(checks.filter(check => check.status === 'failed').map(check => check.checkId)).toEqual([
|
|
84
|
+
'runtime_compatibility',
|
|
85
|
+
'host_liveness_composition',
|
|
86
|
+
'emergency_controls',
|
|
87
|
+
'runtime_shadow_evidence',
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=openclaw-promotion-checks.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-promotion-checks.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/openclaw-promotion-checks.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion-readiness-evaluator.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { REQUIRED_PROMOTION_CHECK_IDS, evaluateRuleCodePromotionReadiness, } from '../index.js';
|
|
3
|
+
const snapshot = {
|
|
4
|
+
snapshotId: 'snap-1',
|
|
5
|
+
snapshotDigest: 'sha256:snapshot',
|
|
6
|
+
artifactDigest: 'sha256:artifact',
|
|
7
|
+
lineageRefs: ['task-1', 'run-1'],
|
|
8
|
+
hostRuntimeVersion: 'openclaw@1',
|
|
9
|
+
safetyGateResults: [],
|
|
10
|
+
shadowSummary: { observed: 20, matched: 3, wouldBlock: 1, wouldAllow: 19, requireApproval: 0, autoCorrect: 0, errors: 0, neutralControl: 1, firstObservedAt: '2026-08-20T00:00:00.000Z', lastObservedAt: '2026-08-21T00:00:00.000Z' },
|
|
11
|
+
configurationVersion: 'config-v1',
|
|
12
|
+
redaction: { version: 'v1', rawParametersStored: false },
|
|
13
|
+
createdAt: '2026-08-21T00:00:00.000Z',
|
|
14
|
+
};
|
|
15
|
+
function passingChecks() {
|
|
16
|
+
return REQUIRED_PROMOTION_CHECK_IDS.map(checkId => ({ checkId, status: 'passed' }));
|
|
17
|
+
}
|
|
18
|
+
describe('evaluateRuleCodePromotionReadiness', () => {
|
|
19
|
+
it('returns ready only when every required hard check is present and passed', () => {
|
|
20
|
+
const result = evaluateRuleCodePromotionReadiness({
|
|
21
|
+
evaluationId: 'eval-1', artifactId: 'artifact-1', artifactDigest: 'sha256:artifact',
|
|
22
|
+
evidenceSnapshot: snapshot, checks: passingChecks(),
|
|
23
|
+
});
|
|
24
|
+
expect(result.status).toBe('ready');
|
|
25
|
+
expect(result.failedChecks).toEqual([]);
|
|
26
|
+
expect(result.evidenceSnapshot.safetyGateResults).toHaveLength(REQUIRED_PROMOTION_CHECK_IDS.length);
|
|
27
|
+
});
|
|
28
|
+
it('blocks when a hard Host Liveness check fails', () => {
|
|
29
|
+
const checks = passingChecks().map(check => check.checkId === 'host_liveness_composition'
|
|
30
|
+
? { ...check, status: 'failed', reasonCode: 'neutral_probe_blocked' }
|
|
31
|
+
: check);
|
|
32
|
+
const result = evaluateRuleCodePromotionReadiness({
|
|
33
|
+
evaluationId: 'eval-2', artifactId: 'artifact-1', artifactDigest: 'sha256:artifact',
|
|
34
|
+
evidenceSnapshot: snapshot, checks,
|
|
35
|
+
});
|
|
36
|
+
expect(result.status).toBe('blocked');
|
|
37
|
+
expect(result.failedChecks).toEqual([{ checkId: 'host_liveness_composition', reasonCode: 'neutral_probe_blocked' }]);
|
|
38
|
+
});
|
|
39
|
+
it('is unavailable when a required check is absent instead of treating missing evidence as pass', () => {
|
|
40
|
+
const result = evaluateRuleCodePromotionReadiness({
|
|
41
|
+
evaluationId: 'eval-3', artifactId: 'artifact-1', artifactDigest: 'sha256:artifact',
|
|
42
|
+
evidenceSnapshot: snapshot,
|
|
43
|
+
checks: passingChecks().filter(check => check.checkId !== 'runtime_shadow_evidence'),
|
|
44
|
+
});
|
|
45
|
+
expect(result.status).toBe('unavailable');
|
|
46
|
+
expect(result.failedChecks).toEqual([{ checkId: 'runtime_shadow_evidence', reasonCode: 'required_check_missing' }]);
|
|
47
|
+
});
|
|
48
|
+
it('rejects an evidence snapshot bound to a different artifact digest', () => {
|
|
49
|
+
const result = evaluateRuleCodePromotionReadiness({
|
|
50
|
+
evaluationId: 'eval-4', artifactId: 'artifact-1', artifactDigest: 'sha256:different',
|
|
51
|
+
evidenceSnapshot: snapshot, checks: passingChecks(),
|
|
52
|
+
});
|
|
53
|
+
expect(result.status).toBe('unavailable');
|
|
54
|
+
expect(result.failedChecks).toContainEqual({ checkId: 'evidence_binding', reasonCode: 'artifact_digest_mismatch' });
|
|
55
|
+
});
|
|
56
|
+
it.each([
|
|
57
|
+
['less than 24 hours', { firstObservedAt: '2026-08-20T00:00:00.001Z' }],
|
|
58
|
+
['fewer than 20 eligible evaluations', { observed: 19 }],
|
|
59
|
+
['fewer than 3 matches', { matched: 2 }],
|
|
60
|
+
['no neutral-control sample', { neutralControl: 0 }],
|
|
61
|
+
])('keeps promotion evidence-insufficient when %s', (_name, shadowSummary) => {
|
|
62
|
+
const result = evaluateRuleCodePromotionReadiness({
|
|
63
|
+
evaluationId: 'eval-insufficient', artifactId: 'artifact-1', artifactDigest: 'sha256:artifact',
|
|
64
|
+
evidenceSnapshot: { ...snapshot, shadowSummary: { ...snapshot.shadowSummary, ...shadowSummary } },
|
|
65
|
+
checks: passingChecks(),
|
|
66
|
+
});
|
|
67
|
+
expect(result.status).toBe('evidence_insufficient');
|
|
68
|
+
expect(result.failedChecks).toEqual([]);
|
|
69
|
+
});
|
|
70
|
+
it('turns unresolved unhealthy shadow evidence into a non-overridable hard failure', () => {
|
|
71
|
+
const result = evaluateRuleCodePromotionReadiness({
|
|
72
|
+
evaluationId: 'eval-unhealthy', artifactId: 'artifact-1', artifactDigest: 'sha256:artifact',
|
|
73
|
+
evidenceSnapshot: { ...snapshot, shadowSummary: { ...snapshot.shadowSummary, errors: 1 } },
|
|
74
|
+
checks: passingChecks(),
|
|
75
|
+
});
|
|
76
|
+
expect(result.status).toBe('blocked');
|
|
77
|
+
expect(result.failedChecks).toContainEqual({
|
|
78
|
+
checkId: 'runtime_shadow_evidence',
|
|
79
|
+
reasonCode: 'unresolved_shadow_unhealthy_evidence',
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=promotion-readiness-evaluator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion-readiness-evaluator.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/promotion-readiness-evaluator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion-readiness-reader.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/promotion-readiness-reader.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { PromotionReadinessReader, REQUIRED_PROMOTION_CHECK_IDS } from '../index.js';
|
|
3
|
+
const activation = {
|
|
4
|
+
activationId: 'act-1', idempotencyKey: 'key-1', artifactId: 'art-1', channel: 'code_tool_hook',
|
|
5
|
+
action: 'code_tool_hook_shadow_activate', targetRef: 'impl://rule-1', activatedAt: '2026-08-21T00:00:00Z', deactivatedAt: null,
|
|
6
|
+
};
|
|
7
|
+
const artifact = {
|
|
8
|
+
artifactId: 'art-1', artifactKind: 'rule', sourceTaskId: 'task-1', lineageArtifactIds: ['parent-1'],
|
|
9
|
+
validationStatus: 'validated', contentJson: '{"implementationCode":"code"}',
|
|
10
|
+
createdAt: '2026-08-21T00:00:00Z', updatedAt: '2026-08-21T00:00:00Z',
|
|
11
|
+
};
|
|
12
|
+
describe('PromotionReadinessReader', () => {
|
|
13
|
+
it('returns granular unavailable checks when host facts are absent', async () => {
|
|
14
|
+
const reader = new PromotionReadinessReader({
|
|
15
|
+
listCodeToolHookActivations: async () => [activation],
|
|
16
|
+
getArtifactById: async () => artifact,
|
|
17
|
+
computeArtifactDigest: () => 'sha256:artifact',
|
|
18
|
+
validateProductionArtifact: async () => ({ ok: true, riskLevel: 'high' }),
|
|
19
|
+
collectHostChecks: async () => [],
|
|
20
|
+
buildEvidenceSnapshot: checks => ({
|
|
21
|
+
snapshotId: 'snap-1', snapshotDigest: 'sha256:snapshot', artifactDigest: 'sha256:artifact',
|
|
22
|
+
lineageRefs: ['task-1', 'parent-1'], hostRuntimeVersion: 'unavailable', safetyGateResults: checks,
|
|
23
|
+
shadowSummary: { observed: 0, matched: 0, wouldBlock: 0, wouldAllow: 0, requireApproval: 0, autoCorrect: 0, errors: 0, neutralControl: 0, firstObservedAt: null, lastObservedAt: null }, configurationVersion: 'config-v1',
|
|
24
|
+
redaction: { version: 'v1', rawParametersStored: false }, createdAt: '2026-08-21T00:00:00Z',
|
|
25
|
+
}),
|
|
26
|
+
newEvaluationId: () => 'eval-1',
|
|
27
|
+
});
|
|
28
|
+
const result = await reader.evaluate({ activationId: 'act-1', expectedArtifactId: 'art-1', expectedArtifactDigest: 'sha256:artifact' });
|
|
29
|
+
expect(result.status).toBe('unavailable');
|
|
30
|
+
expect(result.failedChecks.map(check => check.checkId)).toEqual(expect.arrayContaining(REQUIRED_PROMOTION_CHECK_IDS.filter(id => !['activation_eligibility', 'lineage_binding', 'production_compile_load', 'golden_trace'].includes(id))));
|
|
31
|
+
});
|
|
32
|
+
it('blocks a stale artifact binding before production validation', async () => {
|
|
33
|
+
let validationCalls = 0;
|
|
34
|
+
const reader = new PromotionReadinessReader({
|
|
35
|
+
listCodeToolHookActivations: async () => [activation], getArtifactById: async () => artifact,
|
|
36
|
+
computeArtifactDigest: () => 'sha256:current',
|
|
37
|
+
validateProductionArtifact: async () => { validationCalls += 1; return { ok: true, riskLevel: 'high' }; },
|
|
38
|
+
collectHostChecks: async () => [],
|
|
39
|
+
buildEvidenceSnapshot: checks => ({ snapshotId: 'snap', snapshotDigest: 'digest', artifactDigest: 'sha256:current', lineageRefs: [], hostRuntimeVersion: 'unavailable', safetyGateResults: checks, shadowSummary: { observed: 0, matched: 0, wouldBlock: 0, wouldAllow: 0, requireApproval: 0, autoCorrect: 0, errors: 0, neutralControl: 0, firstObservedAt: null, lastObservedAt: null }, configurationVersion: 'v1', redaction: { version: 'v1', rawParametersStored: false }, createdAt: 'now' }),
|
|
40
|
+
newEvaluationId: () => 'eval-2',
|
|
41
|
+
});
|
|
42
|
+
const result = await reader.evaluate({ activationId: 'act-1', expectedArtifactId: 'art-1', expectedArtifactDigest: 'sha256:stale' });
|
|
43
|
+
expect(result.status).toBe('blocked');
|
|
44
|
+
expect(result.failedChecks).toContainEqual({ checkId: 'lineage_binding', reasonCode: 'artifact_digest_mismatch' });
|
|
45
|
+
expect(validationCalls).toBe(0);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=promotion-readiness-reader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion-readiness-reader.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/promotion-readiness-reader.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rulecode-owner-decision-service.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { RuleCodeOwnerDecisionService } from '../rulecode-owner-decision-service.js';
|
|
3
|
+
const actor = {
|
|
4
|
+
principal: { kind: 'configured_owner', ownerId: 'owner-1' },
|
|
5
|
+
authentication: { method: 'cli_owner_credential', credentialId: 'credential-1' },
|
|
6
|
+
operator: { kind: 'local_user', operatorId: 'Administrator' },
|
|
7
|
+
};
|
|
8
|
+
const ready = {
|
|
9
|
+
status: 'ready',
|
|
10
|
+
evaluationId: 'readiness-1',
|
|
11
|
+
artifactId: 'artifact-1',
|
|
12
|
+
artifactDigest: 'sha256:artifact',
|
|
13
|
+
evidenceSnapshot: {
|
|
14
|
+
snapshotId: 'snapshot-1',
|
|
15
|
+
snapshotDigest: 'sha256:snapshot',
|
|
16
|
+
artifactDigest: 'sha256:artifact',
|
|
17
|
+
lineageRefs: ['task-1', 'run-1'],
|
|
18
|
+
hostRuntimeVersion: 'openclaw@1',
|
|
19
|
+
safetyGateResults: [{ checkId: 'host_liveness', status: 'passed' }],
|
|
20
|
+
shadowSummary: { observed: 20, matched: 3, wouldBlock: 1, wouldAllow: 19, requireApproval: 0, autoCorrect: 0, errors: 0, neutralControl: 1, firstObservedAt: '2026-08-20T00:00:00.000Z', lastObservedAt: '2026-08-21T00:00:00.000Z' },
|
|
21
|
+
configurationVersion: 'config-v1',
|
|
22
|
+
redaction: { version: 'redaction-v1', rawParametersStored: false },
|
|
23
|
+
createdAt: '2026-08-21T02:00:00.000Z',
|
|
24
|
+
},
|
|
25
|
+
failedChecks: [],
|
|
26
|
+
};
|
|
27
|
+
function service(input) {
|
|
28
|
+
const commitPromotion = vi.fn(async (_value) => ({
|
|
29
|
+
activationId: 'activation-1', decisionId: 'decision-1', promotedAt: '2026-08-21T02:01:00.000Z',
|
|
30
|
+
}));
|
|
31
|
+
return {
|
|
32
|
+
commitPromotion,
|
|
33
|
+
instance: new RuleCodeOwnerDecisionService({
|
|
34
|
+
ownerLiveDecisionEnabled: () => input?.ownerEnabled ?? true,
|
|
35
|
+
safetyControlsEnabled: () => input?.safetyEnabled ?? true,
|
|
36
|
+
evaluateReadiness: async () => input?.readiness ?? ready,
|
|
37
|
+
commitPromotion,
|
|
38
|
+
newDecisionId: () => 'decision-1',
|
|
39
|
+
now: () => '2026-08-21T02:01:00.000Z',
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const request = {
|
|
44
|
+
activationId: 'activation-1',
|
|
45
|
+
expectedArtifactId: 'artifact-1',
|
|
46
|
+
expectedArtifactDigest: 'sha256:artifact',
|
|
47
|
+
expectedControlVersion: 1,
|
|
48
|
+
idempotencyKey: 'promote-activation-1-v1',
|
|
49
|
+
reasonCode: 'owner_accepts_shadow_evidence',
|
|
50
|
+
note: 'Reviewed shadow evidence and host-liveness results.',
|
|
51
|
+
confirmed: true,
|
|
52
|
+
};
|
|
53
|
+
describe('RuleCodeOwnerDecisionService promotion authority', () => {
|
|
54
|
+
it('refuses promotion when the Owner decision feature is disabled without calling legacy mutation', async () => {
|
|
55
|
+
const { instance, commitPromotion } = service({ ownerEnabled: false });
|
|
56
|
+
await expect(instance.promote(request, actor)).resolves.toMatchObject({
|
|
57
|
+
ok: false, reasonCode: 'feature_not_enabled', nextAction: expect.stringContaining('rulecode_owner_live_decision'),
|
|
58
|
+
});
|
|
59
|
+
expect(commitPromotion).not.toHaveBeenCalled();
|
|
60
|
+
});
|
|
61
|
+
it('refuses promotion when safety authority is unavailable', async () => {
|
|
62
|
+
const { instance, commitPromotion } = service({ safetyEnabled: false });
|
|
63
|
+
await expect(instance.promote(request, actor)).resolves.toMatchObject({
|
|
64
|
+
ok: false, reasonCode: 'safety_controls_unavailable', nextAction: expect.any(String),
|
|
65
|
+
});
|
|
66
|
+
expect(commitPromotion).not.toHaveBeenCalled();
|
|
67
|
+
});
|
|
68
|
+
it('refuses local break-glass identity from writing an Owner governance decision', async () => {
|
|
69
|
+
const { instance, commitPromotion } = service();
|
|
70
|
+
await expect(instance.promote(request, {
|
|
71
|
+
principal: { kind: 'break_glass', reason: 'local_no_auth_emergency' },
|
|
72
|
+
authentication: { method: 'local_break_glass' },
|
|
73
|
+
})).resolves.toMatchObject({ ok: false, reasonCode: 'owner_authentication_required' });
|
|
74
|
+
expect(commitPromotion).not.toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
it('refuses blocked or unavailable readiness without committing', async () => {
|
|
77
|
+
for (const status of ['blocked', 'unavailable']) {
|
|
78
|
+
const { instance, commitPromotion } = service({ readiness: {
|
|
79
|
+
...ready, status, failedChecks: [{ checkId: 'host_liveness', reasonCode: 'host_contract_unavailable' }],
|
|
80
|
+
} });
|
|
81
|
+
await expect(instance.promote(request, actor)).resolves.toMatchObject({
|
|
82
|
+
ok: false, reasonCode: status === 'blocked' ? 'promotion_safety_gate_blocked' : 'promotion_readiness_unavailable',
|
|
83
|
+
failedChecks: [{ checkId: 'host_liveness', reasonCode: 'host_contract_unavailable' }],
|
|
84
|
+
});
|
|
85
|
+
expect(commitPromotion).not.toHaveBeenCalled();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
it('requires a note for every CLI Owner promotion before readiness evaluation', async () => {
|
|
89
|
+
const { instance, commitPromotion } = service();
|
|
90
|
+
await expect(instance.promote({ ...request, note: ' ' }, actor)).resolves.toMatchObject({
|
|
91
|
+
ok: false, reasonCode: 'cli_owner_note_required', nextAction: expect.stringContaining('note'),
|
|
92
|
+
});
|
|
93
|
+
expect(commitPromotion).not.toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
it('binds authenticated Owner identity and evidence into the sole atomic commit', async () => {
|
|
96
|
+
const { instance, commitPromotion } = service();
|
|
97
|
+
await expect(instance.promote(request, actor)).resolves.toEqual({
|
|
98
|
+
ok: true, decision: 'promoted', activationId: 'activation-1', decisionId: 'decision-1', promotedAt: '2026-08-21T02:01:00.000Z',
|
|
99
|
+
});
|
|
100
|
+
expect(commitPromotion).toHaveBeenCalledOnce();
|
|
101
|
+
expect(commitPromotion).toHaveBeenCalledWith(expect.objectContaining({
|
|
102
|
+
expectedControlVersion: 1,
|
|
103
|
+
idempotencyKey: 'promote-activation-1-v1',
|
|
104
|
+
evidenceSnapshot: ready.evidenceSnapshot,
|
|
105
|
+
decision: expect.objectContaining({
|
|
106
|
+
decisionId: 'decision-1', decision: 'promote_live', principal: actor.principal,
|
|
107
|
+
authentication: actor.authentication, operator: actor.operator,
|
|
108
|
+
evidenceSnapshotId: 'snapshot-1',
|
|
109
|
+
}),
|
|
110
|
+
}));
|
|
111
|
+
});
|
|
112
|
+
it('evaluates a dry-run but never commits a live decision', async () => {
|
|
113
|
+
const { instance, commitPromotion } = service();
|
|
114
|
+
await expect(instance.promote({ ...request, confirmed: false, dryRun: true }, actor)).resolves.toEqual({
|
|
115
|
+
ok: true,
|
|
116
|
+
decision: 'would_promote',
|
|
117
|
+
activationId: 'activation-1',
|
|
118
|
+
readinessEvaluationId: 'readiness-1',
|
|
119
|
+
evidenceSnapshotDigest: 'sha256:snapshot',
|
|
120
|
+
});
|
|
121
|
+
expect(commitPromotion).not.toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
it('refuses an evidence-insufficient promotion without an approved override reason', async () => {
|
|
124
|
+
const { instance, commitPromotion } = service({ readiness: { ...ready, status: 'evidence_insufficient' } });
|
|
125
|
+
await expect(instance.promote({ ...request, reasonCode: 'looks_safe' }, actor)).resolves.toMatchObject({
|
|
126
|
+
ok: false,
|
|
127
|
+
reasonCode: 'evidence_override_reason_required',
|
|
128
|
+
});
|
|
129
|
+
expect(commitPromotion).not.toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
it('allows an evidence-insufficient promotion only with a predefined reason and note', async () => {
|
|
132
|
+
const { instance, commitPromotion } = service({ readiness: { ...ready, status: 'evidence_insufficient' } });
|
|
133
|
+
await expect(instance.promote({
|
|
134
|
+
...request,
|
|
135
|
+
reasonCode: 'controlled_rollout',
|
|
136
|
+
note: 'Limited rollout with immediate emergency controls verified.',
|
|
137
|
+
}, actor)).resolves.toMatchObject({ ok: true, decision: 'promoted' });
|
|
138
|
+
expect(commitPromotion).toHaveBeenCalledOnce();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
//# sourceMappingURL=rulecode-owner-decision-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rulecode-owner-decision-service.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/rulecode-owner-decision-service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rulecode-safety-circuit-breaker.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { evaluateRuleCodeSafetyCircuit, initialRuleCodeSafetyCircuitState } from '../rulecode-safety-circuit-breaker.js';
|
|
3
|
+
describe('RuleCode safety circuit breaker', () => {
|
|
4
|
+
it('trips immediately for protected controls and runtime health failures', () => {
|
|
5
|
+
expect(evaluateRuleCodeSafetyCircuit(initialRuleCodeSafetyCircuitState(), { toolName: 'pd', decision: 'allow', outsideApprovedScope: false, protectedCapabilityMatched: true }).trip).toBe('protected_capability_matched');
|
|
6
|
+
expect(evaluateRuleCodeSafetyCircuit(initialRuleCodeSafetyCircuitState(), { toolName: 'write', decision: 'error', outsideApprovedScope: false, healthFailure: 'timeout' }).trip).toBe('runtime_health_failure');
|
|
7
|
+
});
|
|
8
|
+
it('trips after three consecutive errors and resets the sequence on success', () => {
|
|
9
|
+
let state = initialRuleCodeSafetyCircuitState();
|
|
10
|
+
for (let index = 0; index < 2; index += 1) {
|
|
11
|
+
({ state } = evaluateRuleCodeSafetyCircuit(state, { toolName: 'write', decision: 'error', outsideApprovedScope: false }));
|
|
12
|
+
}
|
|
13
|
+
expect(evaluateRuleCodeSafetyCircuit(state, { toolName: 'write', decision: 'error', outsideApprovedScope: false }).trip).toBe('three_consecutive_errors');
|
|
14
|
+
expect(evaluateRuleCodeSafetyCircuit(state, { toolName: 'write', decision: 'allow', outsideApprovedScope: false }).state.consecutiveErrors).toBe(0);
|
|
15
|
+
});
|
|
16
|
+
it('trips for five consecutive blocks spanning three tools', () => {
|
|
17
|
+
let state = initialRuleCodeSafetyCircuitState();
|
|
18
|
+
for (const toolName of ['write', 'bash', 'write', 'agent']) {
|
|
19
|
+
({ state } = evaluateRuleCodeSafetyCircuit(state, { toolName, decision: 'block', outsideApprovedScope: false }));
|
|
20
|
+
}
|
|
21
|
+
expect(evaluateRuleCodeSafetyCircuit(state, { toolName: 'bash', decision: 'block', outsideApprovedScope: false }).trip).toBe('broad_consecutive_blocking');
|
|
22
|
+
});
|
|
23
|
+
it('trips when more than eighty percent of the latest twenty calls are blocked outside scope', () => {
|
|
24
|
+
let state = initialRuleCodeSafetyCircuitState();
|
|
25
|
+
for (let index = 0; index < 19; index += 1) {
|
|
26
|
+
({ state } = evaluateRuleCodeSafetyCircuit(state, { toolName: `tool-${index}`, decision: index < 17 ? 'block' : 'allow', outsideApprovedScope: index < 17 }));
|
|
27
|
+
}
|
|
28
|
+
expect(evaluateRuleCodeSafetyCircuit(state, { toolName: 'last', decision: 'allow', outsideApprovedScope: false }).trip).toBe('outside_scope_block_ratio');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=rulecode-safety-circuit-breaker.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rulecode-safety-circuit-breaker.test.js","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/rulecode-safety-circuit-breaker.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-activation-safety-store.test.d.ts","sourceRoot":"","sources":["../../../../src/runtime-v2/activation/__tests__/sqlite-activation-safety-store.test.ts"],"names":[],"mappings":""}
|