@lssm/example.kb-update-pipeline 0.0.0-canary-20251213172311 → 0.0.0-canary-20251215231151
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/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +5 -5
- package/README.md +1 -0
- package/package.json +2 -2
- package/src/contracts/index.ts +0 -2
- package/src/contracts/pipeline.ts +20 -7
- package/src/docs/index.ts +0 -2
- package/src/docs/kb-update-pipeline.docblock.ts +2 -3
- package/src/entities/index.ts +0 -2
- package/src/entities/models.ts +13 -5
- package/src/events.ts +20 -7
- package/src/example.ts +0 -2
- package/src/feature.ts +0 -2
- package/src/handlers/index.ts +0 -2
- package/src/handlers/memory.handlers.test.ts +4 -3
- package/src/handlers/memory.handlers.ts +23 -17
- package/src/index.ts +0 -2
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/tsdown.config.js +1 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
$ bun build:bundle && bun build:types
|
|
2
2
|
$ tsdown
|
|
3
|
-
[34mℹ[39m tsdown [2mv0.17.
|
|
3
|
+
[34mℹ[39m tsdown [2mv0.17.4[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
4
4
|
[34mℹ[39m config file: [4m/home/runner/work/contractspec/contractspec/packages/examples/kb-update-pipeline/tsdown.config.js[24m
|
|
5
5
|
[34mℹ[39m entry: [34msrc/events.ts, src/example.ts, src/feature.ts, src/index.ts, src/contracts/index.ts, src/contracts/pipeline.ts, src/docs/index.ts, src/docs/kb-update-pipeline.docblock.ts, src/entities/index.ts, src/entities/models.ts, src/handlers/index.ts, src/handlers/memory.handlers.ts[39m
|
|
6
6
|
[34mℹ[39m target: [34mesnext[39m
|
|
@@ -19,5 +19,5 @@ $ tsdown
|
|
|
19
19
|
[34mℹ[39m [2mdist/[22m[1mhandlers/index.js[22m [2m0.17 kB[22m [2m│ gzip: 0.11 kB[22m
|
|
20
20
|
[34mℹ[39m [2mdist/[22m[1mdocs/index.js[22m [2m0.04 kB[22m [2m│ gzip: 0.06 kB[22m
|
|
21
21
|
[34mℹ[39m 12 files, total: 13.72 kB
|
|
22
|
-
[32m✔[39m Build complete in [
|
|
22
|
+
[32m✔[39m Build complete in [32m41ms[39m
|
|
23
23
|
$ tsc --noEmit
|
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# @lssm/example.kb-update-pipeline
|
|
2
2
|
|
|
3
|
-
## 0.0.0-canary-
|
|
3
|
+
## 0.0.0-canary-20251215231151
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
7
|
- Updated dependencies [3086383]
|
|
8
|
-
- @lssm/lib.contracts@0.0.0-canary-
|
|
9
|
-
- @lssm/lib.schema@0.0.0-canary-
|
|
10
|
-
- @lssm/lib.identity-rbac@0.0.0-canary-
|
|
11
|
-
- @lssm/module.notifications@0.0.0-canary-
|
|
8
|
+
- @lssm/lib.contracts@0.0.0-canary-20251215231151
|
|
9
|
+
- @lssm/lib.schema@0.0.0-canary-20251215231151
|
|
10
|
+
- @lssm/lib.identity-rbac@0.0.0-canary-20251215231151
|
|
11
|
+
- @lssm/module.notifications@0.0.0-canary-20251215231151
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lssm/example.kb-update-pipeline",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-20251215231151",
|
|
4
4
|
"description": "Example: KB update automation pipeline with HITL review and auditability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@lssm/tool.tsdown": "workspace:*",
|
|
44
44
|
"@lssm/tool.typescript": "workspace:*",
|
|
45
|
-
"tsdown": "^0.17.
|
|
45
|
+
"tsdown": "^0.17.4",
|
|
46
46
|
"typescript": "^5.9.3"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
package/src/contracts/index.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { defineCommand } from '@lssm/lib.contracts';
|
|
2
2
|
import { ScalarTypeEnum, defineSchemaModel } from '@lssm/lib.schema';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ChangeCandidateModel,
|
|
6
|
+
ReviewDecisionEnum,
|
|
7
|
+
ReviewTaskModel,
|
|
8
|
+
} from '../entities/models';
|
|
5
9
|
|
|
6
10
|
const RunWatchInput = defineSchemaModel({
|
|
7
11
|
name: 'KbPipelineRunWatchInput',
|
|
@@ -15,7 +19,11 @@ const RunWatchOutput = defineSchemaModel({
|
|
|
15
19
|
name: 'KbPipelineRunWatchOutput',
|
|
16
20
|
description: 'Output containing detected changes.',
|
|
17
21
|
fields: {
|
|
18
|
-
candidates: {
|
|
22
|
+
candidates: {
|
|
23
|
+
type: ChangeCandidateModel,
|
|
24
|
+
isArray: true,
|
|
25
|
+
isOptional: false,
|
|
26
|
+
},
|
|
19
27
|
},
|
|
20
28
|
});
|
|
21
29
|
|
|
@@ -23,7 +31,10 @@ const CreateReviewTaskInput = defineSchemaModel({
|
|
|
23
31
|
name: 'KbPipelineCreateReviewTaskInput',
|
|
24
32
|
description: 'Create a review task for a change candidate.',
|
|
25
33
|
fields: {
|
|
26
|
-
changeCandidateId: {
|
|
34
|
+
changeCandidateId: {
|
|
35
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
36
|
+
isOptional: false,
|
|
37
|
+
},
|
|
27
38
|
},
|
|
28
39
|
});
|
|
29
40
|
|
|
@@ -34,13 +45,17 @@ const SubmitDecisionInput = defineSchemaModel({
|
|
|
34
45
|
reviewTaskId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
35
46
|
decision: { type: ReviewDecisionEnum, isOptional: false },
|
|
36
47
|
decidedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
37
|
-
decidedByRole: {
|
|
48
|
+
decidedByRole: {
|
|
49
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
50
|
+
isOptional: false,
|
|
51
|
+
},
|
|
38
52
|
},
|
|
39
53
|
});
|
|
40
54
|
|
|
41
55
|
const PublishIfReadyInput = defineSchemaModel({
|
|
42
56
|
name: 'KbPipelinePublishIfReadyInput',
|
|
43
|
-
description:
|
|
57
|
+
description:
|
|
58
|
+
'Publish snapshot if approvals are satisfied for a jurisdiction.',
|
|
44
59
|
fields: {
|
|
45
60
|
jurisdiction: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
46
61
|
},
|
|
@@ -142,5 +157,3 @@ export const KbPipelinePublishIfReadyContract = defineCommand({
|
|
|
142
157
|
},
|
|
143
158
|
policy: { auth: 'user' },
|
|
144
159
|
});
|
|
145
|
-
|
|
146
|
-
|
package/src/docs/index.ts
CHANGED
|
@@ -17,7 +17,8 @@ const docBlocks: DocBlock[] = [
|
|
|
17
17
|
{
|
|
18
18
|
id: 'docs.examples.kb-update-pipeline.reference',
|
|
19
19
|
title: 'KB Update Pipeline — Reference',
|
|
20
|
-
summary:
|
|
20
|
+
summary:
|
|
21
|
+
'Entities, contracts, and events for the KB update pipeline example.',
|
|
21
22
|
kind: 'reference',
|
|
22
23
|
visibility: 'public',
|
|
23
24
|
route: '/docs/examples/kb-update-pipeline',
|
|
@@ -27,5 +28,3 @@ const docBlocks: DocBlock[] = [
|
|
|
27
28
|
];
|
|
28
29
|
|
|
29
30
|
registerDocBlocks(docBlocks);
|
|
30
|
-
|
|
31
|
-
|
package/src/entities/index.ts
CHANGED
package/src/entities/models.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ScalarTypeEnum,
|
|
3
|
+
defineEnum,
|
|
4
|
+
defineSchemaModel,
|
|
5
|
+
} from '@lssm/lib.schema';
|
|
2
6
|
|
|
3
7
|
export const ChangeRiskLevelEnum = defineEnum('ChangeRiskLevel', [
|
|
4
8
|
'low',
|
|
@@ -21,7 +25,10 @@ export const ChangeCandidateModel = defineSchemaModel({
|
|
|
21
25
|
description: 'Candidate change detected in a source document.',
|
|
22
26
|
fields: {
|
|
23
27
|
id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
24
|
-
sourceDocumentId: {
|
|
28
|
+
sourceDocumentId: {
|
|
29
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
30
|
+
isOptional: false,
|
|
31
|
+
},
|
|
25
32
|
detectedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
26
33
|
diffSummary: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
27
34
|
riskLevel: { type: ChangeRiskLevelEnum, isOptional: false },
|
|
@@ -33,7 +40,10 @@ export const ReviewTaskModel = defineSchemaModel({
|
|
|
33
40
|
description: 'Human verification task for a change candidate.',
|
|
34
41
|
fields: {
|
|
35
42
|
id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
36
|
-
changeCandidateId: {
|
|
43
|
+
changeCandidateId: {
|
|
44
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
45
|
+
isOptional: false,
|
|
46
|
+
},
|
|
37
47
|
status: { type: ScalarTypeEnum.String_unsecure(), isOptional: false }, // open|decided
|
|
38
48
|
assignedRole: { type: ReviewAssignedRoleEnum, isOptional: false },
|
|
39
49
|
decision: { type: ReviewDecisionEnum, isOptional: true },
|
|
@@ -41,5 +51,3 @@ export const ReviewTaskModel = defineSchemaModel({
|
|
|
41
51
|
decidedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
42
52
|
},
|
|
43
53
|
});
|
|
44
|
-
|
|
45
|
-
|
package/src/events.ts
CHANGED
|
@@ -5,8 +5,14 @@ const KbChangeDetectedPayload = defineSchemaModel({
|
|
|
5
5
|
name: 'KbChangeDetectedPayload',
|
|
6
6
|
description: 'Emitted when a source change is detected.',
|
|
7
7
|
fields: {
|
|
8
|
-
changeCandidateId: {
|
|
9
|
-
|
|
8
|
+
changeCandidateId: {
|
|
9
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
10
|
+
isOptional: false,
|
|
11
|
+
},
|
|
12
|
+
sourceDocumentId: {
|
|
13
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
14
|
+
isOptional: false,
|
|
15
|
+
},
|
|
10
16
|
riskLevel: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
11
17
|
},
|
|
12
18
|
});
|
|
@@ -22,7 +28,10 @@ const KbChangeSummarizedPayload = defineSchemaModel({
|
|
|
22
28
|
name: 'KbChangeSummarizedPayload',
|
|
23
29
|
description: 'Emitted when a change summary is produced.',
|
|
24
30
|
fields: {
|
|
25
|
-
changeCandidateId: {
|
|
31
|
+
changeCandidateId: {
|
|
32
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
33
|
+
isOptional: false,
|
|
34
|
+
},
|
|
26
35
|
summary: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
27
36
|
riskLevel: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
28
37
|
},
|
|
@@ -39,7 +48,10 @@ const KbPatchProposedPayload = defineSchemaModel({
|
|
|
39
48
|
name: 'KbPatchProposedPayload',
|
|
40
49
|
description: 'Emitted when draft rule patches are proposed.',
|
|
41
50
|
fields: {
|
|
42
|
-
changeCandidateId: {
|
|
51
|
+
changeCandidateId: {
|
|
52
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
53
|
+
isOptional: false,
|
|
54
|
+
},
|
|
43
55
|
proposedRuleVersionIds: {
|
|
44
56
|
type: ScalarTypeEnum.String_unsecure(),
|
|
45
57
|
isArray: true,
|
|
@@ -60,7 +72,10 @@ const KbReviewRequestedPayload = defineSchemaModel({
|
|
|
60
72
|
description: 'Emitted when a review is requested.',
|
|
61
73
|
fields: {
|
|
62
74
|
reviewTaskId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
63
|
-
changeCandidateId: {
|
|
75
|
+
changeCandidateId: {
|
|
76
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
77
|
+
isOptional: false,
|
|
78
|
+
},
|
|
64
79
|
assignedRole: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
65
80
|
},
|
|
66
81
|
});
|
|
@@ -88,5 +103,3 @@ export const KbReviewDecidedEvent = defineEvent({
|
|
|
88
103
|
description: 'KB review decided.',
|
|
89
104
|
payload: KbReviewDecidedPayload,
|
|
90
105
|
});
|
|
91
|
-
|
|
92
|
-
|
package/src/example.ts
CHANGED
package/src/feature.ts
CHANGED
package/src/handlers/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from 'bun:test';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createPipelineMemoryHandlers,
|
|
5
|
+
createPipelineMemoryStore,
|
|
6
|
+
} from './memory.handlers';
|
|
4
7
|
|
|
5
8
|
describe('@lssm/example.kb-update-pipeline memory handlers', () => {
|
|
6
9
|
it('high-risk change cannot be approved by curator role', async () => {
|
|
@@ -76,5 +79,3 @@ describe('@lssm/example.kb-update-pipeline memory handlers', () => {
|
|
|
76
79
|
).rejects.toThrow('NOT_READY');
|
|
77
80
|
});
|
|
78
81
|
});
|
|
79
|
-
|
|
80
|
-
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
interface ChangeCandidate {
|
|
2
2
|
id: string;
|
|
3
3
|
sourceDocumentId: string;
|
|
4
4
|
detectedAt: Date;
|
|
5
5
|
diffSummary: string;
|
|
6
6
|
riskLevel: 'low' | 'medium' | 'high';
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface ReviewTask {
|
|
10
10
|
id: string;
|
|
11
11
|
changeCandidateId: string;
|
|
12
12
|
status: 'open' | 'decided';
|
|
@@ -14,20 +14,20 @@ type ReviewTask = {
|
|
|
14
14
|
decision?: 'approve' | 'reject';
|
|
15
15
|
decidedAt?: Date;
|
|
16
16
|
decidedBy?: string;
|
|
17
|
-
}
|
|
17
|
+
}
|
|
18
18
|
|
|
19
19
|
export interface PipelineMemoryStore {
|
|
20
20
|
candidates: Map<string, ChangeCandidate>;
|
|
21
21
|
reviewTasks: Map<string, ReviewTask>;
|
|
22
22
|
proposedRuleVersionIdsByCandidate: Map<string, string[]>;
|
|
23
23
|
approvedRuleVersionIds: Set<string>;
|
|
24
|
-
notifications:
|
|
24
|
+
notifications: {
|
|
25
25
|
kind: 'kb.review.requested';
|
|
26
26
|
reviewTaskId: string;
|
|
27
27
|
changeCandidateId: string;
|
|
28
28
|
assignedRole: 'curator' | 'expert';
|
|
29
29
|
createdAt: Date;
|
|
30
|
-
}
|
|
30
|
+
}[];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function createPipelineMemoryStore(): PipelineMemoryStore {
|
|
@@ -45,20 +45,26 @@ function stableId(prefix: string, value: string): string {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export interface PipelineMemoryHandlers {
|
|
48
|
-
runWatch(input: {
|
|
48
|
+
runWatch(input: {
|
|
49
|
+
jurisdiction: string;
|
|
50
|
+
}): Promise<{ candidates: ChangeCandidate[] }>;
|
|
49
51
|
createReviewTask(input: { changeCandidateId: string }): Promise<ReviewTask>;
|
|
50
52
|
proposeRulePatch(input: {
|
|
51
53
|
changeCandidateId: string;
|
|
52
54
|
proposedRuleVersionIds: string[];
|
|
53
55
|
}): Promise<{ proposedRuleVersionIds: string[] }>;
|
|
54
|
-
markRuleVersionApproved(input: {
|
|
56
|
+
markRuleVersionApproved(input: {
|
|
57
|
+
ruleVersionId: string;
|
|
58
|
+
}): Promise<{ ruleVersionId: string }>;
|
|
55
59
|
submitDecision(input: {
|
|
56
60
|
reviewTaskId: string;
|
|
57
61
|
decision: 'approve' | 'reject';
|
|
58
62
|
decidedBy: string;
|
|
59
63
|
decidedByRole: 'curator' | 'expert';
|
|
60
64
|
}): Promise<ReviewTask>;
|
|
61
|
-
publishIfReady(input: {
|
|
65
|
+
publishIfReady(input: {
|
|
66
|
+
jurisdiction: string;
|
|
67
|
+
}): Promise<{ published: boolean; reason?: string }>;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
export function createPipelineMemoryHandlers(
|
|
@@ -104,10 +110,9 @@ export function createPipelineMemoryHandlers(
|
|
|
104
110
|
if (!store.candidates.has(input.changeCandidateId)) {
|
|
105
111
|
throw new Error('CHANGE_CANDIDATE_NOT_FOUND');
|
|
106
112
|
}
|
|
107
|
-
store.proposedRuleVersionIdsByCandidate.set(
|
|
108
|
-
input.
|
|
109
|
-
|
|
110
|
-
);
|
|
113
|
+
store.proposedRuleVersionIdsByCandidate.set(input.changeCandidateId, [
|
|
114
|
+
...input.proposedRuleVersionIds,
|
|
115
|
+
]);
|
|
111
116
|
return { proposedRuleVersionIds: [...input.proposedRuleVersionIds] };
|
|
112
117
|
}
|
|
113
118
|
|
|
@@ -158,8 +163,11 @@ export function createPipelineMemoryHandlers(
|
|
|
158
163
|
for (const task of store.reviewTasks.values()) {
|
|
159
164
|
if (task.decision !== 'approve') continue;
|
|
160
165
|
const proposed =
|
|
161
|
-
store.proposedRuleVersionIdsByCandidate.get(task.changeCandidateId) ??
|
|
162
|
-
|
|
166
|
+
store.proposedRuleVersionIdsByCandidate.get(task.changeCandidateId) ??
|
|
167
|
+
[];
|
|
168
|
+
const unapproved = proposed.filter(
|
|
169
|
+
(id) => !store.approvedRuleVersionIds.has(id)
|
|
170
|
+
);
|
|
163
171
|
if (unapproved.length) {
|
|
164
172
|
throw new Error('NOT_READY');
|
|
165
173
|
}
|
|
@@ -176,5 +184,3 @@ export function createPipelineMemoryHandlers(
|
|
|
176
184
|
publishIfReady,
|
|
177
185
|
};
|
|
178
186
|
}
|
|
179
|
-
|
|
180
|
-
|
package/src/index.ts
CHANGED
package/tsconfig.json
CHANGED