@quolu/lattice 0.52.2 → 0.52.4
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/package.json +1 -1
- package/src/artifact-contracts-v2.mjs +325 -325
- package/src/artifact-contracts.mjs +895 -895
- package/src/boundary-compiler.mjs +712 -712
- package/src/boundary-observation-compiler-v2.mjs +344 -344
- package/src/bounded-seam.mjs +230 -230
- package/src/bridge-address.mjs +107 -107
- package/src/bridge-cli.mjs +297 -297
- package/src/bridge-config.mjs +346 -346
- package/src/bridge-daemon.mjs +378 -378
- package/src/bridge-hub-protocol.mjs +198 -0
- package/src/bridge-launch-agent.mjs +323 -323
- package/src/bridge-registrar.mjs +102 -102
- package/src/bridge-server.mjs +376 -376
- package/src/cli-help.mjs +308 -308
- package/src/cli-stdio.mjs +40 -40
- package/src/control-compiler.mjs +532 -532
- package/src/dag-chain.mjs +261 -261
- package/src/factory-diagnostics.mjs +188 -188
- package/src/git-process.mjs +72 -0
- package/src/hash-chain.mjs +76 -76
- package/src/hooks-cli.mjs +1057 -1057
- package/src/isolation-runner.mjs +409 -409
- package/src/node-version-guard.mjs +44 -44
- package/src/project-cli.mjs +699 -697
- package/src/project-identity.mjs +130 -130
- package/src/rc1-black-box-oracle.mjs +906 -906
- package/src/rc1-comparison.mjs +154 -154
- package/src/rc1-evidence-bundle.mjs +456 -456
- package/src/rc1-v4-campaign.mjs +708 -708
- package/src/rc1-v4-transform.mjs +467 -467
- package/src/rc1-v5-artifact-set.mjs +855 -855
- package/src/rc1-v5-behavior-evidence.mjs +594 -594
- package/src/rc1-v5-campaign.mjs +797 -797
- package/src/rc1-v5-transform.mjs +421 -421
- package/src/rc1-v6-artifact-set.mjs +807 -807
- package/src/rc1-v6-behavior-evidence.mjs +273 -273
- package/src/rc1-v6-campaign.mjs +623 -623
- package/src/rc1-v6-causal-binding.mjs +473 -473
- package/src/rc1-v6-measurement.mjs +313 -313
- package/src/rc2-artifact-set.mjs +1584 -1584
- package/src/rc2-campaign.mjs +1506 -1506
- package/src/rc2-delivery-policy-front-end.mjs +1079 -1079
- package/src/rc2-delivery-policy-oracle.mjs +134 -134
- package/src/rc2-delivery-policy-transform.mjs +1127 -1127
- package/src/rc2-rc1-transfer-front-end.mjs +511 -511
- package/src/rc3-actual-dogfood.mjs +652 -652
- package/src/rc3-dogfood-scaffold.mjs +315 -315
- package/src/rc3-scripted-campaign.mjs +1383 -1383
- package/src/rc4-stage1-dogfood.mjs +673 -673
- package/src/runtime-adapter-registry.mjs +524 -524
- package/src/runtime-cli.mjs +4588 -4588
- package/src/runtime-contracts.mjs +824 -824
- package/src/runtime-control-store.mjs +604 -604
- package/src/runtime-controller-protocol.mjs +587 -587
- package/src/runtime-decision-verifier.mjs +701 -701
- package/src/runtime-diff-observer.mjs +361 -361
- package/src/runtime-direct-os-observer.mjs +301 -301
- package/src/runtime-driver-state.mjs +166 -166
- package/src/runtime-engine.mjs +779 -779
- package/src/runtime-errors.mjs +356 -356
- package/src/runtime-event-store.mjs +189 -189
- package/src/runtime-front-end.mjs +925 -925
- package/src/runtime-gate-store.mjs +481 -481
- package/src/runtime-hold-recompile.mjs +916 -916
- package/src/runtime-io-sentinel.mjs +391 -391
- package/src/runtime-lifecycle-lock.mjs +294 -294
- package/src/runtime-managed-supervisor.mjs +1490 -1490
- package/src/runtime-multi-epoch-store.mjs +838 -838
- package/src/runtime-projection.mjs +269 -269
- package/src/runtime-pull-intake.mjs +1192 -1192
- package/src/runtime-scripted-adapter-controller.mjs +1160 -1160
- package/src/runtime-scripted-executor.mjs +163 -163
- package/src/runtime-scripted-worktree.mjs +104 -104
- package/src/runtime-seam-resolve.mjs +428 -428
- package/src/runtime-seam-treatment.mjs +173 -173
- package/src/runtime-socket-owner.mjs +125 -125
- package/src/runtime-work-order-contracts.mjs +91 -91
- package/src/runtime-work-order-controller.mjs +1171 -1171
- package/src/runtime-worktree-executor.mjs +199 -199
- package/src/schedulability-compiler-v2.mjs +303 -303
- package/src/schedulability-verifier-v2.mjs +317 -317
- package/src/seam-apply.mjs +549 -549
- package/src/seam-commit-shared.mjs +22 -22
- package/src/seam-commit-transform.mjs +81 -81
- package/src/seam-commit.mjs +18 -18
- package/src/seam-cost.mjs +322 -322
- package/src/seam-derivation.mjs +188 -188
- package/src/seam-gate.mjs +146 -146
- package/src/seam-proposal-contracts.mjs +446 -446
- package/src/seam-proposal-queries.mjs +521 -521
- package/src/seam-proposal.mjs +2011 -2011
- package/src/seam-ref.mjs +33 -33
- package/src/seam-rewrite.mjs +286 -286
- package/src/seam-transform.mjs +554 -554
- package/src/seam-verification.mjs +260 -260
- package/src/sensor-adapter.mjs +432 -432
- package/src/sensor-cli.mjs +139 -139
- package/src/sensor-diff.mjs +661 -661
- package/src/sensor-node-runtime.mjs +53 -53
- package/src/sensor-runtime.mjs +52 -52
- package/src/timestamp-contract.mjs +8 -8
- package/src/todo-audit-pending.mjs +91 -91
- package/src/todo-chain.mjs +178 -178
- package/src/todo-cli.mjs +3143 -3141
- package/src/todo-contracts.mjs +728 -728
- package/src/todo-dashboard-registry.mjs +573 -573
- package/src/todo-dispatch-shape.mjs +190 -190
- package/src/todo-gantt-html-independence.mjs +239 -239
- package/src/todo-gantt-html-shared.mjs +226 -226
- package/src/todo-gantt-html-style.mjs +131 -131
- package/src/todo-gantt-html.mjs +248 -248
- package/src/todo-gantt-layout.mjs +974 -974
- package/src/todo-gantt-live.mjs +361 -361
- package/src/todo-gantt-nested.mjs +263 -263
- package/src/todo-gantt-presentation.mjs +217 -217
- package/src/todo-gantt-scope.mjs +123 -123
- package/src/todo-gantt-svg.mjs +353 -353
- package/src/todo-independence-contracts.mjs +595 -595
- package/src/todo-independence-guidance.mjs +322 -322
- package/src/todo-independence.mjs +640 -640
- package/src/todo-markdown-renderer.mjs +260 -260
- package/src/todo-migration.mjs +448 -448
- package/src/todo-narrative-anchor.mjs +130 -130
- package/src/todo-note-store.mjs +629 -629
- package/src/todo-parallel-candidates.mjs +114 -114
- package/src/todo-revision.mjs +995 -995
- package/src/todo-split.mjs +472 -472
- package/src/todo-status.mjs +690 -690
- package/src/todo-store-git-transaction.mjs +418 -418
- package/src/todo-store.mjs +4360 -4322
- package/src/treatment-compiler.mjs +728 -728
- package/src/treatment-runner.mjs +656 -656
- package/src/witness-scaffold.mjs +180 -180
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
import { canonicalizeArtifact } from './artifact-contracts.mjs';
|
|
2
|
-
import {
|
|
3
|
-
RUN_EVENT_KINDS,
|
|
4
|
-
validateRunEvent,
|
|
5
|
-
} from './runtime-contracts.mjs';
|
|
6
|
-
import { digestWithoutField, verifyLinearHashChain } from './hash-chain.mjs';
|
|
7
|
-
|
|
8
|
-
// RC3 event store契約(ADR 0044 Decision 3)。
|
|
9
|
-
// event storeはrun単位のappend-only canonical event列であり、`sequence`は0起点の
|
|
10
|
-
// 連番、`previous_digest`は直前eventの`event_digest`(先頭はnull)とするdigest chainを
|
|
11
|
-
// 持つ。gap、重複、fork、digest不一致、未知kind、redaction違反をtyped rejectする。
|
|
12
|
-
// runtime stateは保存prefixからのprojection(runtime-projection.mjs)としてのみ再構成する。
|
|
13
|
-
|
|
14
|
-
export { RUN_EVENT_KINDS };
|
|
15
|
-
|
|
16
|
-
// ADR 0044 Decision 3.5 redaction契約: credential、token、cookie、prompt全文、
|
|
17
|
-
// 無関係な会話をevent payloadへ保存しない。key名はcase-insensitiveの語幹部分一致
|
|
18
|
-
// (`authToken`等のalias迂回を塞ぐ)、valueは既知のsecret形式patternで検出する。
|
|
19
|
-
// これは防波堤であって秘匿性の証明ではない。拡張はrun_event.v2+新ADRで行う。
|
|
20
|
-
export const REDACTION_FORBIDDEN_PAYLOAD_KEYS = Object.freeze([
|
|
21
|
-
'api_key',
|
|
22
|
-
'apikey',
|
|
23
|
-
'authorization',
|
|
24
|
-
'bearer',
|
|
25
|
-
'chat_log',
|
|
26
|
-
'conversation',
|
|
27
|
-
'cookie',
|
|
28
|
-
'credential',
|
|
29
|
-
'oauth',
|
|
30
|
-
'passphrase',
|
|
31
|
-
'password',
|
|
32
|
-
'private_key',
|
|
33
|
-
'prompt',
|
|
34
|
-
'secret',
|
|
35
|
-
'token',
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
const FORBIDDEN_VALUE_PATTERNS = Object.freeze([
|
|
39
|
-
/^bearer\s+\S+/iu,
|
|
40
|
-
/-----BEGIN [A-Z ]*PRIVATE KEY-----/u,
|
|
41
|
-
/^eyJ[0-9A-Za-z_-]{10,}\./u,
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
function forbiddenKey(key) {
|
|
45
|
-
const lowered = key.toLowerCase();
|
|
46
|
-
return REDACTION_FORBIDDEN_PAYLOAD_KEYS.some((stem) => lowered.includes(stem));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function forbiddenValue(value) {
|
|
50
|
-
return typeof value === 'string'
|
|
51
|
-
&& FORBIDDEN_VALUE_PATTERNS.some((pattern) => pattern.test(value));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** eventの正規byte列(sorted key・LF・UTF-8のcanonical JSON)を返す。 */
|
|
55
|
-
export function canonicalizeRunEvent(event) {
|
|
56
|
-
return canonicalizeArtifact(event);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* event自己digest: `event_digest`自身を除いた残り全fieldのcanonical JSON SHA-256。
|
|
61
|
-
* chain構築側は`event_digest`未設定のeventを渡してよい。
|
|
62
|
-
*/
|
|
63
|
-
export function digestRunEvent(event) {
|
|
64
|
-
if (event === null || typeof event !== 'object' || Array.isArray(event)) {
|
|
65
|
-
throw new TypeError('run event契約違反: eventはplain objectでなければならない');
|
|
66
|
-
}
|
|
67
|
-
return digestWithoutField(event, 'event_digest', canonicalizeArtifact);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* payload treeから禁止key(語幹部分一致)と禁止value patternを検出する。
|
|
72
|
-
* violationは`{ pointer, key }`で返し、空配列だけをredaction contract充足とみなす。
|
|
73
|
-
*/
|
|
74
|
-
export function findRedactionViolations(payload, pointer = '') {
|
|
75
|
-
const violations = [];
|
|
76
|
-
if (Array.isArray(payload)) {
|
|
77
|
-
payload.forEach((entry, index) => {
|
|
78
|
-
violations.push(...findRedactionViolations(entry, `${pointer}/${index}`));
|
|
79
|
-
});
|
|
80
|
-
return violations;
|
|
81
|
-
}
|
|
82
|
-
if (payload === null || typeof payload !== 'object') {
|
|
83
|
-
if (forbiddenValue(payload)) {
|
|
84
|
-
violations.push({ pointer, key: null });
|
|
85
|
-
}
|
|
86
|
-
return violations;
|
|
87
|
-
}
|
|
88
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
89
|
-
const childPointer = `${pointer}/${key}`;
|
|
90
|
-
if (forbiddenKey(key)) {
|
|
91
|
-
violations.push({ pointer: childPointer, key });
|
|
92
|
-
}
|
|
93
|
-
violations.push(...findRedactionViolations(value, childPointer));
|
|
94
|
-
}
|
|
95
|
-
return violations;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function check(id, passed) {
|
|
99
|
-
return { id, passed };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* 保存bytesだけからevent chainを検査する。summary boolean・in-memory stateを
|
|
104
|
-
* 証拠にしない(ADR 0044 Decision 3.3)。
|
|
105
|
-
* @returns {{ schema: string, valid: boolean, checks: Array<{id: string, passed: boolean}>,
|
|
106
|
-
* failed_conditions: string[] }}
|
|
107
|
-
*/
|
|
108
|
-
export function verifyRunEventChain(options = {}) {
|
|
109
|
-
const checks = [];
|
|
110
|
-
let events = null;
|
|
111
|
-
let inputOk = false;
|
|
112
|
-
try {
|
|
113
|
-
inputOk = options !== null
|
|
114
|
-
&& typeof options === 'object'
|
|
115
|
-
&& !Array.isArray(options)
|
|
116
|
-
&& Object.keys(options).length === 1
|
|
117
|
-
&& Array.isArray(options.events)
|
|
118
|
-
&& options.events.length > 0;
|
|
119
|
-
if (inputOk) {
|
|
120
|
-
events = options.events;
|
|
121
|
-
canonicalizeArtifact(events);
|
|
122
|
-
}
|
|
123
|
-
} catch {
|
|
124
|
-
inputOk = false;
|
|
125
|
-
}
|
|
126
|
-
checks.push(check('input_shape', inputOk));
|
|
127
|
-
|
|
128
|
-
const shapeOk = inputOk && events.every((event) => {
|
|
129
|
-
// kind・digest・chainは専用conditionで区別するため、shape検査から除いて判定する。
|
|
130
|
-
if (event === null || typeof event !== 'object' || Array.isArray(event)) return false;
|
|
131
|
-
const probe = {
|
|
132
|
-
...event,
|
|
133
|
-
kind: RUN_EVENT_KINDS[0],
|
|
134
|
-
sequence: 0,
|
|
135
|
-
previous_digest: null,
|
|
136
|
-
};
|
|
137
|
-
probe.event_digest = digestWithoutField(probe, 'event_digest', canonicalizeArtifact);
|
|
138
|
-
return validateRunEvent(probe);
|
|
139
|
-
});
|
|
140
|
-
checks.push(check('event_shape', shapeOk));
|
|
141
|
-
|
|
142
|
-
const runIdentityOk = shapeOk && new Set(events.map((event) => event.run_id)).size === 1;
|
|
143
|
-
checks.push(check('run_identity', runIdentityOk));
|
|
144
|
-
|
|
145
|
-
const knownKindOk = shapeOk && events.every((event) => RUN_EVENT_KINDS.includes(event.kind));
|
|
146
|
-
checks.push(check('unknown_kind', knownKindOk));
|
|
147
|
-
|
|
148
|
-
const sharedFailures = shapeOk ? verifyLinearHashChain({
|
|
149
|
-
entries: events,
|
|
150
|
-
canonicalize: canonicalizeArtifact,
|
|
151
|
-
digestField: 'event_digest',
|
|
152
|
-
}) : new Set();
|
|
153
|
-
const eventDigestOk = shapeOk && !sharedFailures.has('event_digest_mismatch');
|
|
154
|
-
checks.push(check('event_digest_mismatch', eventDigestOk));
|
|
155
|
-
|
|
156
|
-
const redactionOk = shapeOk && events.every((event) => (
|
|
157
|
-
findRedactionViolations(event.payload).length === 0
|
|
158
|
-
));
|
|
159
|
-
checks.push(check('payload_redaction', redactionOk));
|
|
160
|
-
|
|
161
|
-
const storageOrderOk = shapeOk && !sharedFailures.has('storage_order');
|
|
162
|
-
checks.push(check('storage_order', storageOrderOk));
|
|
163
|
-
|
|
164
|
-
let duplicateOk = shapeOk;
|
|
165
|
-
let forkOk = shapeOk;
|
|
166
|
-
let gapOk = shapeOk;
|
|
167
|
-
let genesisOk = shapeOk;
|
|
168
|
-
let chainOk = shapeOk;
|
|
169
|
-
if (shapeOk) {
|
|
170
|
-
duplicateOk = !sharedFailures.has('duplicate_event');
|
|
171
|
-
forkOk = !sharedFailures.has('sequence_fork');
|
|
172
|
-
gapOk = !sharedFailures.has('sequence_gap');
|
|
173
|
-
genesisOk = !sharedFailures.has('genesis_binding');
|
|
174
|
-
chainOk = !sharedFailures.has('digest_chain_mismatch');
|
|
175
|
-
}
|
|
176
|
-
checks.push(check('duplicate_event', duplicateOk));
|
|
177
|
-
checks.push(check('sequence_fork', forkOk));
|
|
178
|
-
checks.push(check('sequence_gap', gapOk));
|
|
179
|
-
checks.push(check('genesis_binding', genesisOk));
|
|
180
|
-
checks.push(check('digest_chain_mismatch', chainOk));
|
|
181
|
-
|
|
182
|
-
const failedConditions = checks.filter(({ passed }) => !passed).map(({ id }) => id);
|
|
183
|
-
return {
|
|
184
|
-
schema: 'lattice.runtime_event_chain_verification.v1',
|
|
185
|
-
valid: failedConditions.length === 0,
|
|
186
|
-
checks,
|
|
187
|
-
failed_conditions: failedConditions,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
1
|
+
import { canonicalizeArtifact } from './artifact-contracts.mjs';
|
|
2
|
+
import {
|
|
3
|
+
RUN_EVENT_KINDS,
|
|
4
|
+
validateRunEvent,
|
|
5
|
+
} from './runtime-contracts.mjs';
|
|
6
|
+
import { digestWithoutField, verifyLinearHashChain } from './hash-chain.mjs';
|
|
7
|
+
|
|
8
|
+
// RC3 event store契約(ADR 0044 Decision 3)。
|
|
9
|
+
// event storeはrun単位のappend-only canonical event列であり、`sequence`は0起点の
|
|
10
|
+
// 連番、`previous_digest`は直前eventの`event_digest`(先頭はnull)とするdigest chainを
|
|
11
|
+
// 持つ。gap、重複、fork、digest不一致、未知kind、redaction違反をtyped rejectする。
|
|
12
|
+
// runtime stateは保存prefixからのprojection(runtime-projection.mjs)としてのみ再構成する。
|
|
13
|
+
|
|
14
|
+
export { RUN_EVENT_KINDS };
|
|
15
|
+
|
|
16
|
+
// ADR 0044 Decision 3.5 redaction契約: credential、token、cookie、prompt全文、
|
|
17
|
+
// 無関係な会話をevent payloadへ保存しない。key名はcase-insensitiveの語幹部分一致
|
|
18
|
+
// (`authToken`等のalias迂回を塞ぐ)、valueは既知のsecret形式patternで検出する。
|
|
19
|
+
// これは防波堤であって秘匿性の証明ではない。拡張はrun_event.v2+新ADRで行う。
|
|
20
|
+
export const REDACTION_FORBIDDEN_PAYLOAD_KEYS = Object.freeze([
|
|
21
|
+
'api_key',
|
|
22
|
+
'apikey',
|
|
23
|
+
'authorization',
|
|
24
|
+
'bearer',
|
|
25
|
+
'chat_log',
|
|
26
|
+
'conversation',
|
|
27
|
+
'cookie',
|
|
28
|
+
'credential',
|
|
29
|
+
'oauth',
|
|
30
|
+
'passphrase',
|
|
31
|
+
'password',
|
|
32
|
+
'private_key',
|
|
33
|
+
'prompt',
|
|
34
|
+
'secret',
|
|
35
|
+
'token',
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const FORBIDDEN_VALUE_PATTERNS = Object.freeze([
|
|
39
|
+
/^bearer\s+\S+/iu,
|
|
40
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/u,
|
|
41
|
+
/^eyJ[0-9A-Za-z_-]{10,}\./u,
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
function forbiddenKey(key) {
|
|
45
|
+
const lowered = key.toLowerCase();
|
|
46
|
+
return REDACTION_FORBIDDEN_PAYLOAD_KEYS.some((stem) => lowered.includes(stem));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function forbiddenValue(value) {
|
|
50
|
+
return typeof value === 'string'
|
|
51
|
+
&& FORBIDDEN_VALUE_PATTERNS.some((pattern) => pattern.test(value));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** eventの正規byte列(sorted key・LF・UTF-8のcanonical JSON)を返す。 */
|
|
55
|
+
export function canonicalizeRunEvent(event) {
|
|
56
|
+
return canonicalizeArtifact(event);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* event自己digest: `event_digest`自身を除いた残り全fieldのcanonical JSON SHA-256。
|
|
61
|
+
* chain構築側は`event_digest`未設定のeventを渡してよい。
|
|
62
|
+
*/
|
|
63
|
+
export function digestRunEvent(event) {
|
|
64
|
+
if (event === null || typeof event !== 'object' || Array.isArray(event)) {
|
|
65
|
+
throw new TypeError('run event契約違反: eventはplain objectでなければならない');
|
|
66
|
+
}
|
|
67
|
+
return digestWithoutField(event, 'event_digest', canonicalizeArtifact);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* payload treeから禁止key(語幹部分一致)と禁止value patternを検出する。
|
|
72
|
+
* violationは`{ pointer, key }`で返し、空配列だけをredaction contract充足とみなす。
|
|
73
|
+
*/
|
|
74
|
+
export function findRedactionViolations(payload, pointer = '') {
|
|
75
|
+
const violations = [];
|
|
76
|
+
if (Array.isArray(payload)) {
|
|
77
|
+
payload.forEach((entry, index) => {
|
|
78
|
+
violations.push(...findRedactionViolations(entry, `${pointer}/${index}`));
|
|
79
|
+
});
|
|
80
|
+
return violations;
|
|
81
|
+
}
|
|
82
|
+
if (payload === null || typeof payload !== 'object') {
|
|
83
|
+
if (forbiddenValue(payload)) {
|
|
84
|
+
violations.push({ pointer, key: null });
|
|
85
|
+
}
|
|
86
|
+
return violations;
|
|
87
|
+
}
|
|
88
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
89
|
+
const childPointer = `${pointer}/${key}`;
|
|
90
|
+
if (forbiddenKey(key)) {
|
|
91
|
+
violations.push({ pointer: childPointer, key });
|
|
92
|
+
}
|
|
93
|
+
violations.push(...findRedactionViolations(value, childPointer));
|
|
94
|
+
}
|
|
95
|
+
return violations;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function check(id, passed) {
|
|
99
|
+
return { id, passed };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 保存bytesだけからevent chainを検査する。summary boolean・in-memory stateを
|
|
104
|
+
* 証拠にしない(ADR 0044 Decision 3.3)。
|
|
105
|
+
* @returns {{ schema: string, valid: boolean, checks: Array<{id: string, passed: boolean}>,
|
|
106
|
+
* failed_conditions: string[] }}
|
|
107
|
+
*/
|
|
108
|
+
export function verifyRunEventChain(options = {}) {
|
|
109
|
+
const checks = [];
|
|
110
|
+
let events = null;
|
|
111
|
+
let inputOk = false;
|
|
112
|
+
try {
|
|
113
|
+
inputOk = options !== null
|
|
114
|
+
&& typeof options === 'object'
|
|
115
|
+
&& !Array.isArray(options)
|
|
116
|
+
&& Object.keys(options).length === 1
|
|
117
|
+
&& Array.isArray(options.events)
|
|
118
|
+
&& options.events.length > 0;
|
|
119
|
+
if (inputOk) {
|
|
120
|
+
events = options.events;
|
|
121
|
+
canonicalizeArtifact(events);
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
inputOk = false;
|
|
125
|
+
}
|
|
126
|
+
checks.push(check('input_shape', inputOk));
|
|
127
|
+
|
|
128
|
+
const shapeOk = inputOk && events.every((event) => {
|
|
129
|
+
// kind・digest・chainは専用conditionで区別するため、shape検査から除いて判定する。
|
|
130
|
+
if (event === null || typeof event !== 'object' || Array.isArray(event)) return false;
|
|
131
|
+
const probe = {
|
|
132
|
+
...event,
|
|
133
|
+
kind: RUN_EVENT_KINDS[0],
|
|
134
|
+
sequence: 0,
|
|
135
|
+
previous_digest: null,
|
|
136
|
+
};
|
|
137
|
+
probe.event_digest = digestWithoutField(probe, 'event_digest', canonicalizeArtifact);
|
|
138
|
+
return validateRunEvent(probe);
|
|
139
|
+
});
|
|
140
|
+
checks.push(check('event_shape', shapeOk));
|
|
141
|
+
|
|
142
|
+
const runIdentityOk = shapeOk && new Set(events.map((event) => event.run_id)).size === 1;
|
|
143
|
+
checks.push(check('run_identity', runIdentityOk));
|
|
144
|
+
|
|
145
|
+
const knownKindOk = shapeOk && events.every((event) => RUN_EVENT_KINDS.includes(event.kind));
|
|
146
|
+
checks.push(check('unknown_kind', knownKindOk));
|
|
147
|
+
|
|
148
|
+
const sharedFailures = shapeOk ? verifyLinearHashChain({
|
|
149
|
+
entries: events,
|
|
150
|
+
canonicalize: canonicalizeArtifact,
|
|
151
|
+
digestField: 'event_digest',
|
|
152
|
+
}) : new Set();
|
|
153
|
+
const eventDigestOk = shapeOk && !sharedFailures.has('event_digest_mismatch');
|
|
154
|
+
checks.push(check('event_digest_mismatch', eventDigestOk));
|
|
155
|
+
|
|
156
|
+
const redactionOk = shapeOk && events.every((event) => (
|
|
157
|
+
findRedactionViolations(event.payload).length === 0
|
|
158
|
+
));
|
|
159
|
+
checks.push(check('payload_redaction', redactionOk));
|
|
160
|
+
|
|
161
|
+
const storageOrderOk = shapeOk && !sharedFailures.has('storage_order');
|
|
162
|
+
checks.push(check('storage_order', storageOrderOk));
|
|
163
|
+
|
|
164
|
+
let duplicateOk = shapeOk;
|
|
165
|
+
let forkOk = shapeOk;
|
|
166
|
+
let gapOk = shapeOk;
|
|
167
|
+
let genesisOk = shapeOk;
|
|
168
|
+
let chainOk = shapeOk;
|
|
169
|
+
if (shapeOk) {
|
|
170
|
+
duplicateOk = !sharedFailures.has('duplicate_event');
|
|
171
|
+
forkOk = !sharedFailures.has('sequence_fork');
|
|
172
|
+
gapOk = !sharedFailures.has('sequence_gap');
|
|
173
|
+
genesisOk = !sharedFailures.has('genesis_binding');
|
|
174
|
+
chainOk = !sharedFailures.has('digest_chain_mismatch');
|
|
175
|
+
}
|
|
176
|
+
checks.push(check('duplicate_event', duplicateOk));
|
|
177
|
+
checks.push(check('sequence_fork', forkOk));
|
|
178
|
+
checks.push(check('sequence_gap', gapOk));
|
|
179
|
+
checks.push(check('genesis_binding', genesisOk));
|
|
180
|
+
checks.push(check('digest_chain_mismatch', chainOk));
|
|
181
|
+
|
|
182
|
+
const failedConditions = checks.filter(({ passed }) => !passed).map(({ id }) => id);
|
|
183
|
+
return {
|
|
184
|
+
schema: 'lattice.runtime_event_chain_verification.v1',
|
|
185
|
+
valid: failedConditions.length === 0,
|
|
186
|
+
checks,
|
|
187
|
+
failed_conditions: failedConditions,
|
|
188
|
+
};
|
|
189
|
+
}
|