sysprom 1.14.0 → 1.15.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/README.md +19 -2
- package/dist/src/cli/commands/infer.d.ts +2 -0
- package/dist/src/cli/commands/infer.js +206 -0
- package/dist/src/cli/program.js +2 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/mcp/server.js +74 -1
- package/dist/src/operations/index.d.ts +4 -0
- package/dist/src/operations/index.js +5 -0
- package/dist/src/operations/infer-completeness.d.ts +414 -0
- package/dist/src/operations/infer-completeness.js +131 -0
- package/dist/src/operations/infer-derived.d.ts +375 -0
- package/dist/src/operations/infer-derived.js +158 -0
- package/dist/src/operations/infer-impact.d.ts +1246 -0
- package/dist/src/operations/infer-impact.js +144 -0
- package/dist/src/operations/infer-lifecycle.d.ts +421 -0
- package/dist/src/operations/infer-lifecycle.js +119 -0
- package/package.json +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { defineOperation } from "./define-operation.js";
|
|
3
|
+
import { Node, SysProMDocument } from "../schema.js";
|
|
4
|
+
/**
|
|
5
|
+
* Impact node in the impact trace.
|
|
6
|
+
*/
|
|
7
|
+
const ImpactNode = z.object({
|
|
8
|
+
id: z.string(),
|
|
9
|
+
node: Node.optional(),
|
|
10
|
+
impactType: z.enum(["direct", "transitive", "potential"]),
|
|
11
|
+
distance: z.number().int().nonnegative(),
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Output schema for inferImpactOp.
|
|
15
|
+
*/
|
|
16
|
+
const ImpactOutput = z.object({
|
|
17
|
+
sourceId: z.string(),
|
|
18
|
+
impactedNodes: z.array(ImpactNode),
|
|
19
|
+
summary: z.object({
|
|
20
|
+
direct: z.number(),
|
|
21
|
+
transitive: z.number(),
|
|
22
|
+
potential: z.number(),
|
|
23
|
+
total: z.number(),
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Relationship types that indicate impact propagation.
|
|
28
|
+
*/
|
|
29
|
+
const IMPACT_RELATIONSHIPS = new Set([
|
|
30
|
+
"affects",
|
|
31
|
+
"depends_on",
|
|
32
|
+
"modifies",
|
|
33
|
+
"constrained_by",
|
|
34
|
+
"requires",
|
|
35
|
+
"produces",
|
|
36
|
+
"consumes",
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Relationship types that indicate potential impact (weaker signal).
|
|
40
|
+
*/
|
|
41
|
+
const POTENTIAL_IMPACT_RELATIONSHIPS = new Set([
|
|
42
|
+
"part_of",
|
|
43
|
+
"governed_by",
|
|
44
|
+
"must_follow",
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* Infer impact from a starting node through the graph.
|
|
48
|
+
*
|
|
49
|
+
* Traces impact propagation through affect, dependency, and modification
|
|
50
|
+
* relationships, categorising nodes as directly impacted, transitively
|
|
51
|
+
* impacted, or potentially impacted.
|
|
52
|
+
*/
|
|
53
|
+
export const inferImpactOp = defineOperation({
|
|
54
|
+
name: "infer-impact",
|
|
55
|
+
description: "Infer impact from a node through the graph following impact relationships",
|
|
56
|
+
input: z.object({
|
|
57
|
+
doc: SysProMDocument,
|
|
58
|
+
startId: z.string(),
|
|
59
|
+
}),
|
|
60
|
+
output: ImpactOutput,
|
|
61
|
+
fn: (input) => {
|
|
62
|
+
const nodeMap = new Map(input.doc.nodes.map((n) => [n.id, n]));
|
|
63
|
+
const visited = new Set();
|
|
64
|
+
const impactedNodes = [];
|
|
65
|
+
// Mark source as visited to exclude it from results
|
|
66
|
+
visited.add(input.startId);
|
|
67
|
+
// BFS traversal with impact type tracking
|
|
68
|
+
const queue = [];
|
|
69
|
+
// Start with direct impacts
|
|
70
|
+
const directImpacts = (input.doc.relationships ?? [])
|
|
71
|
+
.filter((r) => r.from === input.startId && IMPACT_RELATIONSHIPS.has(r.type))
|
|
72
|
+
.map((r) => r.to);
|
|
73
|
+
const potentialImpacts = (input.doc.relationships ?? [])
|
|
74
|
+
.filter((r) => r.from === input.startId &&
|
|
75
|
+
POTENTIAL_IMPACT_RELATIONSHIPS.has(r.type))
|
|
76
|
+
.map((r) => r.to);
|
|
77
|
+
// Add direct impacts to queue
|
|
78
|
+
for (const id of directImpacts) {
|
|
79
|
+
queue.push({ id, distance: 1, impactType: "direct" });
|
|
80
|
+
}
|
|
81
|
+
// Add potential impacts to queue
|
|
82
|
+
for (const id of potentialImpacts) {
|
|
83
|
+
if (!directImpacts.includes(id)) {
|
|
84
|
+
queue.push({ id, distance: 1, impactType: "potential" });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// BFS traversal
|
|
88
|
+
while (queue.length > 0) {
|
|
89
|
+
const current = queue.shift();
|
|
90
|
+
if (!current)
|
|
91
|
+
break;
|
|
92
|
+
if (visited.has(current.id))
|
|
93
|
+
continue;
|
|
94
|
+
visited.add(current.id);
|
|
95
|
+
const node = nodeMap.get(current.id);
|
|
96
|
+
impactedNodes.push({
|
|
97
|
+
id: current.id,
|
|
98
|
+
node,
|
|
99
|
+
impactType: current.impactType,
|
|
100
|
+
distance: current.distance,
|
|
101
|
+
});
|
|
102
|
+
// Find transitive impacts
|
|
103
|
+
const transitiveImpacts = (input.doc.relationships ?? [])
|
|
104
|
+
.filter((r) => r.from === current.id &&
|
|
105
|
+
(IMPACT_RELATIONSHIPS.has(r.type) ||
|
|
106
|
+
POTENTIAL_IMPACT_RELATIONSHIPS.has(r.type)))
|
|
107
|
+
.map((r) => ({
|
|
108
|
+
id: r.to,
|
|
109
|
+
impactType: current.distance >= 1
|
|
110
|
+
? "transitive"
|
|
111
|
+
: current.impactType,
|
|
112
|
+
}));
|
|
113
|
+
for (const impact of transitiveImpacts) {
|
|
114
|
+
if (!visited.has(impact.id)) {
|
|
115
|
+
queue.push({
|
|
116
|
+
id: impact.id,
|
|
117
|
+
distance: current.distance + 1,
|
|
118
|
+
impactType: impact.impactType,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Sort by distance, then by impact type priority
|
|
124
|
+
const impactPriority = { direct: 0, transitive: 1, potential: 2 };
|
|
125
|
+
impactedNodes.sort((a, b) => {
|
|
126
|
+
if (a.distance !== b.distance)
|
|
127
|
+
return a.distance - b.distance;
|
|
128
|
+
return impactPriority[a.impactType] - impactPriority[b.impactType];
|
|
129
|
+
});
|
|
130
|
+
const summary = {
|
|
131
|
+
direct: impactedNodes.filter((n) => n.impactType === "direct").length,
|
|
132
|
+
transitive: impactedNodes.filter((n) => n.impactType === "transitive")
|
|
133
|
+
.length,
|
|
134
|
+
potential: impactedNodes.filter((n) => n.impactType === "potential")
|
|
135
|
+
.length,
|
|
136
|
+
total: impactedNodes.length,
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
sourceId: input.startId,
|
|
140
|
+
impactedNodes,
|
|
141
|
+
summary,
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
});
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Lifecycle inference result for a single node.
|
|
4
|
+
*/
|
|
5
|
+
declare const LifecycleResult: z.ZodObject<{
|
|
6
|
+
id: z.ZodString;
|
|
7
|
+
type: z.ZodString;
|
|
8
|
+
name: z.ZodString;
|
|
9
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
10
|
+
deprecated: "deprecated";
|
|
11
|
+
proposed: "proposed";
|
|
12
|
+
accepted: "accepted";
|
|
13
|
+
active: "active";
|
|
14
|
+
implemented: "implemented";
|
|
15
|
+
adopted: "adopted";
|
|
16
|
+
defined: "defined";
|
|
17
|
+
introduced: "introduced";
|
|
18
|
+
in_progress: "in_progress";
|
|
19
|
+
complete: "complete";
|
|
20
|
+
consolidated: "consolidated";
|
|
21
|
+
experimental: "experimental";
|
|
22
|
+
retired: "retired";
|
|
23
|
+
superseded: "superseded";
|
|
24
|
+
abandoned: "abandoned";
|
|
25
|
+
deferred: "deferred";
|
|
26
|
+
}> & {
|
|
27
|
+
is(value: unknown): value is "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred";
|
|
28
|
+
}>;
|
|
29
|
+
lifecycle: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
|
|
30
|
+
inferredState: z.ZodString;
|
|
31
|
+
inferredPhase: z.ZodEnum<{
|
|
32
|
+
unknown: "unknown";
|
|
33
|
+
early: "early";
|
|
34
|
+
middle: "middle";
|
|
35
|
+
late: "late";
|
|
36
|
+
terminal: "terminal";
|
|
37
|
+
}>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
/** Lifecycle inference result for a single node. */
|
|
40
|
+
export type LifecycleResult = z.infer<typeof LifecycleResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Output schema for inferLifecycleOp.
|
|
43
|
+
*/
|
|
44
|
+
declare const LifecycleOutput: z.ZodObject<{
|
|
45
|
+
nodes: z.ZodArray<z.ZodObject<{
|
|
46
|
+
id: z.ZodString;
|
|
47
|
+
type: z.ZodString;
|
|
48
|
+
name: z.ZodString;
|
|
49
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
50
|
+
deprecated: "deprecated";
|
|
51
|
+
proposed: "proposed";
|
|
52
|
+
accepted: "accepted";
|
|
53
|
+
active: "active";
|
|
54
|
+
implemented: "implemented";
|
|
55
|
+
adopted: "adopted";
|
|
56
|
+
defined: "defined";
|
|
57
|
+
introduced: "introduced";
|
|
58
|
+
in_progress: "in_progress";
|
|
59
|
+
complete: "complete";
|
|
60
|
+
consolidated: "consolidated";
|
|
61
|
+
experimental: "experimental";
|
|
62
|
+
retired: "retired";
|
|
63
|
+
superseded: "superseded";
|
|
64
|
+
abandoned: "abandoned";
|
|
65
|
+
deferred: "deferred";
|
|
66
|
+
}> & {
|
|
67
|
+
is(value: unknown): value is "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred";
|
|
68
|
+
}>;
|
|
69
|
+
lifecycle: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
|
|
70
|
+
inferredState: z.ZodString;
|
|
71
|
+
inferredPhase: z.ZodEnum<{
|
|
72
|
+
unknown: "unknown";
|
|
73
|
+
early: "early";
|
|
74
|
+
middle: "middle";
|
|
75
|
+
late: "late";
|
|
76
|
+
terminal: "terminal";
|
|
77
|
+
}>;
|
|
78
|
+
}, z.core.$strip>>;
|
|
79
|
+
summary: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
/** Output of lifecycle inference operation. */
|
|
82
|
+
export type LifecycleOutput = z.infer<typeof LifecycleOutput>;
|
|
83
|
+
/**
|
|
84
|
+
* Infer lifecycle state for all nodes based on status and lifecycle fields.
|
|
85
|
+
*
|
|
86
|
+
* Returns inferred state, phase classification, and summary statistics.
|
|
87
|
+
*/
|
|
88
|
+
export declare const inferLifecycleOp: import("./define-operation.js").DefinedOperation<z.ZodObject<{
|
|
89
|
+
doc: z.ZodObject<{
|
|
90
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
91
|
+
metadata: z.ZodOptional<z.ZodObject<{
|
|
92
|
+
title: z.ZodOptional<z.ZodString>;
|
|
93
|
+
doc_type: z.ZodOptional<z.ZodString>;
|
|
94
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
95
|
+
status: z.ZodOptional<z.ZodString>;
|
|
96
|
+
version: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodInt]>>;
|
|
97
|
+
}, z.core.$loose> & {
|
|
98
|
+
is(value: unknown): value is {
|
|
99
|
+
[x: string]: unknown;
|
|
100
|
+
title?: string | undefined;
|
|
101
|
+
doc_type?: string | undefined;
|
|
102
|
+
scope?: string | undefined;
|
|
103
|
+
status?: string | undefined;
|
|
104
|
+
version?: string | number | undefined;
|
|
105
|
+
};
|
|
106
|
+
}>;
|
|
107
|
+
nodes: z.ZodArray<z.ZodObject<{
|
|
108
|
+
id: z.ZodString;
|
|
109
|
+
type: z.ZodEnum<{
|
|
110
|
+
intent: "intent";
|
|
111
|
+
concept: "concept";
|
|
112
|
+
capability: "capability";
|
|
113
|
+
element: "element";
|
|
114
|
+
realisation: "realisation";
|
|
115
|
+
invariant: "invariant";
|
|
116
|
+
principle: "principle";
|
|
117
|
+
policy: "policy";
|
|
118
|
+
protocol: "protocol";
|
|
119
|
+
stage: "stage";
|
|
120
|
+
role: "role";
|
|
121
|
+
gate: "gate";
|
|
122
|
+
mode: "mode";
|
|
123
|
+
artefact: "artefact";
|
|
124
|
+
artefact_flow: "artefact_flow";
|
|
125
|
+
decision: "decision";
|
|
126
|
+
change: "change";
|
|
127
|
+
view: "view";
|
|
128
|
+
milestone: "milestone";
|
|
129
|
+
version: "version";
|
|
130
|
+
}> & {
|
|
131
|
+
is(value: unknown): value is "intent" | "concept" | "capability" | "element" | "realisation" | "invariant" | "principle" | "policy" | "protocol" | "stage" | "role" | "gate" | "mode" | "artefact" | "artefact_flow" | "decision" | "change" | "view" | "milestone" | "version";
|
|
132
|
+
};
|
|
133
|
+
name: z.ZodString;
|
|
134
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
135
|
+
is(value: unknown): value is string | string[];
|
|
136
|
+
}>;
|
|
137
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
138
|
+
deprecated: "deprecated";
|
|
139
|
+
proposed: "proposed";
|
|
140
|
+
accepted: "accepted";
|
|
141
|
+
active: "active";
|
|
142
|
+
implemented: "implemented";
|
|
143
|
+
adopted: "adopted";
|
|
144
|
+
defined: "defined";
|
|
145
|
+
introduced: "introduced";
|
|
146
|
+
in_progress: "in_progress";
|
|
147
|
+
complete: "complete";
|
|
148
|
+
consolidated: "consolidated";
|
|
149
|
+
experimental: "experimental";
|
|
150
|
+
retired: "retired";
|
|
151
|
+
superseded: "superseded";
|
|
152
|
+
abandoned: "abandoned";
|
|
153
|
+
deferred: "deferred";
|
|
154
|
+
}> & {
|
|
155
|
+
is(value: unknown): value is "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred";
|
|
156
|
+
}>;
|
|
157
|
+
lifecycle: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
|
|
158
|
+
context: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
159
|
+
is(value: unknown): value is string | string[];
|
|
160
|
+
}>;
|
|
161
|
+
options: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
162
|
+
id: z.ZodString;
|
|
163
|
+
description: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
164
|
+
is(value: unknown): value is string | string[];
|
|
165
|
+
};
|
|
166
|
+
}, z.core.$loose> & {
|
|
167
|
+
is(value: unknown): value is {
|
|
168
|
+
[x: string]: unknown;
|
|
169
|
+
id: string;
|
|
170
|
+
description: string | string[];
|
|
171
|
+
};
|
|
172
|
+
}>>;
|
|
173
|
+
selected: z.ZodOptional<z.ZodString>;
|
|
174
|
+
rationale: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
175
|
+
is(value: unknown): value is string | string[];
|
|
176
|
+
}>;
|
|
177
|
+
scope: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
178
|
+
operations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
179
|
+
type: z.ZodEnum<{
|
|
180
|
+
link: "link";
|
|
181
|
+
add: "add";
|
|
182
|
+
update: "update";
|
|
183
|
+
remove: "remove";
|
|
184
|
+
}>;
|
|
185
|
+
target: z.ZodOptional<z.ZodString>;
|
|
186
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
187
|
+
is(value: unknown): value is string | string[];
|
|
188
|
+
}>;
|
|
189
|
+
}, z.core.$loose> & {
|
|
190
|
+
is(value: unknown): value is {
|
|
191
|
+
[x: string]: unknown;
|
|
192
|
+
type: "link" | "add" | "update" | "remove";
|
|
193
|
+
target?: string | undefined;
|
|
194
|
+
description?: string | string[] | undefined;
|
|
195
|
+
};
|
|
196
|
+
}>>;
|
|
197
|
+
plan: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
198
|
+
description: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
199
|
+
is(value: unknown): value is string | string[];
|
|
200
|
+
};
|
|
201
|
+
done: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
202
|
+
}, z.core.$loose> & {
|
|
203
|
+
is(value: unknown): value is {
|
|
204
|
+
[x: string]: unknown;
|
|
205
|
+
description: string | string[];
|
|
206
|
+
done?: boolean | undefined;
|
|
207
|
+
};
|
|
208
|
+
}>>;
|
|
209
|
+
propagation: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
210
|
+
includes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
211
|
+
input: z.ZodOptional<z.ZodString>;
|
|
212
|
+
output: z.ZodOptional<z.ZodString>;
|
|
213
|
+
external_references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
214
|
+
role: z.ZodEnum<{
|
|
215
|
+
output: "output";
|
|
216
|
+
input: "input";
|
|
217
|
+
context: "context";
|
|
218
|
+
evidence: "evidence";
|
|
219
|
+
source: "source";
|
|
220
|
+
standard: "standard";
|
|
221
|
+
prior_art: "prior_art";
|
|
222
|
+
}> & {
|
|
223
|
+
is(value: unknown): value is "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
224
|
+
};
|
|
225
|
+
identifier: z.ZodString;
|
|
226
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
227
|
+
is(value: unknown): value is string | string[];
|
|
228
|
+
}>;
|
|
229
|
+
node_id: z.ZodOptional<z.ZodString>;
|
|
230
|
+
internalised: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
231
|
+
is(value: unknown): value is string | string[];
|
|
232
|
+
}>;
|
|
233
|
+
}, z.core.$strip> & {
|
|
234
|
+
is(value: unknown): value is {
|
|
235
|
+
role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
236
|
+
identifier: string;
|
|
237
|
+
description?: string | string[] | undefined;
|
|
238
|
+
node_id?: string | undefined;
|
|
239
|
+
internalised?: string | string[] | undefined;
|
|
240
|
+
};
|
|
241
|
+
}>>;
|
|
242
|
+
readonly subsystem: z.ZodOptional<z.ZodObject</*elided*/ any, z.core.$strip>>;
|
|
243
|
+
}, z.core.$loose>>;
|
|
244
|
+
relationships: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
245
|
+
from: z.ZodString;
|
|
246
|
+
to: z.ZodString;
|
|
247
|
+
type: z.ZodEnum<{
|
|
248
|
+
refines: "refines";
|
|
249
|
+
realises: "realises";
|
|
250
|
+
implements: "implements";
|
|
251
|
+
depends_on: "depends_on";
|
|
252
|
+
constrained_by: "constrained_by";
|
|
253
|
+
affects: "affects";
|
|
254
|
+
supersedes: "supersedes";
|
|
255
|
+
must_preserve: "must_preserve";
|
|
256
|
+
performs: "performs";
|
|
257
|
+
part_of: "part_of";
|
|
258
|
+
precedes: "precedes";
|
|
259
|
+
must_follow: "must_follow";
|
|
260
|
+
blocks: "blocks";
|
|
261
|
+
routes_to: "routes_to";
|
|
262
|
+
governed_by: "governed_by";
|
|
263
|
+
modifies: "modifies";
|
|
264
|
+
triggered_by: "triggered_by";
|
|
265
|
+
applies_to: "applies_to";
|
|
266
|
+
produces: "produces";
|
|
267
|
+
consumes: "consumes";
|
|
268
|
+
transforms_into: "transforms_into";
|
|
269
|
+
selects: "selects";
|
|
270
|
+
requires: "requires";
|
|
271
|
+
disables: "disables";
|
|
272
|
+
}> & {
|
|
273
|
+
is(value: unknown): value is "refines" | "realises" | "implements" | "depends_on" | "constrained_by" | "affects" | "supersedes" | "must_preserve" | "performs" | "part_of" | "precedes" | "must_follow" | "blocks" | "routes_to" | "governed_by" | "modifies" | "triggered_by" | "applies_to" | "produces" | "consumes" | "transforms_into" | "selects" | "requires" | "disables";
|
|
274
|
+
};
|
|
275
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
276
|
+
is(value: unknown): value is string | string[];
|
|
277
|
+
}>;
|
|
278
|
+
}, z.core.$loose> & {
|
|
279
|
+
is(value: unknown): value is {
|
|
280
|
+
[x: string]: unknown;
|
|
281
|
+
from: string;
|
|
282
|
+
to: string;
|
|
283
|
+
type: "refines" | "realises" | "implements" | "depends_on" | "constrained_by" | "affects" | "supersedes" | "must_preserve" | "performs" | "part_of" | "precedes" | "must_follow" | "blocks" | "routes_to" | "governed_by" | "modifies" | "triggered_by" | "applies_to" | "produces" | "consumes" | "transforms_into" | "selects" | "requires" | "disables";
|
|
284
|
+
description?: string | string[] | undefined;
|
|
285
|
+
};
|
|
286
|
+
}>>;
|
|
287
|
+
external_references: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
288
|
+
role: z.ZodEnum<{
|
|
289
|
+
output: "output";
|
|
290
|
+
input: "input";
|
|
291
|
+
context: "context";
|
|
292
|
+
evidence: "evidence";
|
|
293
|
+
source: "source";
|
|
294
|
+
standard: "standard";
|
|
295
|
+
prior_art: "prior_art";
|
|
296
|
+
}> & {
|
|
297
|
+
is(value: unknown): value is "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
298
|
+
};
|
|
299
|
+
identifier: z.ZodString;
|
|
300
|
+
description: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
301
|
+
is(value: unknown): value is string | string[];
|
|
302
|
+
}>;
|
|
303
|
+
node_id: z.ZodOptional<z.ZodString>;
|
|
304
|
+
internalised: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]> & {
|
|
305
|
+
is(value: unknown): value is string | string[];
|
|
306
|
+
}>;
|
|
307
|
+
}, z.core.$strip> & {
|
|
308
|
+
is(value: unknown): value is {
|
|
309
|
+
role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
310
|
+
identifier: string;
|
|
311
|
+
description?: string | string[] | undefined;
|
|
312
|
+
node_id?: string | undefined;
|
|
313
|
+
internalised?: string | string[] | undefined;
|
|
314
|
+
};
|
|
315
|
+
}>>;
|
|
316
|
+
}, z.core.$strip> & {
|
|
317
|
+
is(value: unknown): value is {
|
|
318
|
+
nodes: {
|
|
319
|
+
[x: string]: unknown;
|
|
320
|
+
id: string;
|
|
321
|
+
type: "intent" | "concept" | "capability" | "element" | "realisation" | "invariant" | "principle" | "policy" | "protocol" | "stage" | "role" | "gate" | "mode" | "artefact" | "artefact_flow" | "decision" | "change" | "view" | "milestone" | "version";
|
|
322
|
+
name: string;
|
|
323
|
+
description?: string | string[] | undefined;
|
|
324
|
+
status?: "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred" | undefined;
|
|
325
|
+
lifecycle?: Record<string, string | boolean> | undefined;
|
|
326
|
+
context?: string | string[] | undefined;
|
|
327
|
+
options?: {
|
|
328
|
+
[x: string]: unknown;
|
|
329
|
+
id: string;
|
|
330
|
+
description: string | string[];
|
|
331
|
+
}[] | undefined;
|
|
332
|
+
selected?: string | undefined;
|
|
333
|
+
rationale?: string | string[] | undefined;
|
|
334
|
+
scope?: string[] | undefined;
|
|
335
|
+
operations?: {
|
|
336
|
+
[x: string]: unknown;
|
|
337
|
+
type: "link" | "add" | "update" | "remove";
|
|
338
|
+
target?: string | undefined;
|
|
339
|
+
description?: string | string[] | undefined;
|
|
340
|
+
}[] | undefined;
|
|
341
|
+
plan?: {
|
|
342
|
+
[x: string]: unknown;
|
|
343
|
+
description: string | string[];
|
|
344
|
+
done?: boolean | undefined;
|
|
345
|
+
}[] | undefined;
|
|
346
|
+
propagation?: Record<string, boolean> | undefined;
|
|
347
|
+
includes?: string[] | undefined;
|
|
348
|
+
input?: string | undefined;
|
|
349
|
+
output?: string | undefined;
|
|
350
|
+
external_references?: {
|
|
351
|
+
role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
352
|
+
identifier: string;
|
|
353
|
+
description?: string | string[] | undefined;
|
|
354
|
+
node_id?: string | undefined;
|
|
355
|
+
internalised?: string | string[] | undefined;
|
|
356
|
+
}[] | undefined;
|
|
357
|
+
subsystem?: /*elided*/ any | undefined;
|
|
358
|
+
}[];
|
|
359
|
+
$schema?: string | undefined;
|
|
360
|
+
metadata?: {
|
|
361
|
+
[x: string]: unknown;
|
|
362
|
+
title?: string | undefined;
|
|
363
|
+
doc_type?: string | undefined;
|
|
364
|
+
scope?: string | undefined;
|
|
365
|
+
status?: string | undefined;
|
|
366
|
+
version?: string | number | undefined;
|
|
367
|
+
} | undefined;
|
|
368
|
+
relationships?: {
|
|
369
|
+
[x: string]: unknown;
|
|
370
|
+
from: string;
|
|
371
|
+
to: string;
|
|
372
|
+
type: "refines" | "realises" | "implements" | "depends_on" | "constrained_by" | "affects" | "supersedes" | "must_preserve" | "performs" | "part_of" | "precedes" | "must_follow" | "blocks" | "routes_to" | "governed_by" | "modifies" | "triggered_by" | "applies_to" | "produces" | "consumes" | "transforms_into" | "selects" | "requires" | "disables";
|
|
373
|
+
description?: string | string[] | undefined;
|
|
374
|
+
}[] | undefined;
|
|
375
|
+
external_references?: {
|
|
376
|
+
role: "output" | "input" | "context" | "evidence" | "source" | "standard" | "prior_art";
|
|
377
|
+
identifier: string;
|
|
378
|
+
description?: string | string[] | undefined;
|
|
379
|
+
node_id?: string | undefined;
|
|
380
|
+
internalised?: string | string[] | undefined;
|
|
381
|
+
}[] | undefined;
|
|
382
|
+
};
|
|
383
|
+
};
|
|
384
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
385
|
+
nodes: z.ZodArray<z.ZodObject<{
|
|
386
|
+
id: z.ZodString;
|
|
387
|
+
type: z.ZodString;
|
|
388
|
+
name: z.ZodString;
|
|
389
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
390
|
+
deprecated: "deprecated";
|
|
391
|
+
proposed: "proposed";
|
|
392
|
+
accepted: "accepted";
|
|
393
|
+
active: "active";
|
|
394
|
+
implemented: "implemented";
|
|
395
|
+
adopted: "adopted";
|
|
396
|
+
defined: "defined";
|
|
397
|
+
introduced: "introduced";
|
|
398
|
+
in_progress: "in_progress";
|
|
399
|
+
complete: "complete";
|
|
400
|
+
consolidated: "consolidated";
|
|
401
|
+
experimental: "experimental";
|
|
402
|
+
retired: "retired";
|
|
403
|
+
superseded: "superseded";
|
|
404
|
+
abandoned: "abandoned";
|
|
405
|
+
deferred: "deferred";
|
|
406
|
+
}> & {
|
|
407
|
+
is(value: unknown): value is "deprecated" | "proposed" | "accepted" | "active" | "implemented" | "adopted" | "defined" | "introduced" | "in_progress" | "complete" | "consolidated" | "experimental" | "retired" | "superseded" | "abandoned" | "deferred";
|
|
408
|
+
}>;
|
|
409
|
+
lifecycle: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>>;
|
|
410
|
+
inferredState: z.ZodString;
|
|
411
|
+
inferredPhase: z.ZodEnum<{
|
|
412
|
+
unknown: "unknown";
|
|
413
|
+
early: "early";
|
|
414
|
+
middle: "middle";
|
|
415
|
+
late: "late";
|
|
416
|
+
terminal: "terminal";
|
|
417
|
+
}>;
|
|
418
|
+
}, z.core.$strip>>;
|
|
419
|
+
summary: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
420
|
+
}, z.core.$strip>>;
|
|
421
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { defineOperation } from "./define-operation.js";
|
|
3
|
+
import { SysProMDocument, NodeStatus } from "../schema.js";
|
|
4
|
+
/**
|
|
5
|
+
* Lifecycle inference result for a single node.
|
|
6
|
+
*/
|
|
7
|
+
const LifecycleResult = z.object({
|
|
8
|
+
id: z.string(),
|
|
9
|
+
type: z.string(),
|
|
10
|
+
name: z.string(),
|
|
11
|
+
status: NodeStatus.optional(),
|
|
12
|
+
lifecycle: z
|
|
13
|
+
.record(z.string(), z.union([z.boolean(), z.string()]))
|
|
14
|
+
.optional(),
|
|
15
|
+
inferredState: z.string(),
|
|
16
|
+
inferredPhase: z.enum(["early", "middle", "late", "terminal", "unknown"]),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Output schema for inferLifecycleOp.
|
|
20
|
+
*/
|
|
21
|
+
const LifecycleOutput = z.object({
|
|
22
|
+
nodes: z.array(LifecycleResult),
|
|
23
|
+
summary: z.record(z.string(), z.number()),
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Lifecycle phases based on typical progression.
|
|
27
|
+
*/
|
|
28
|
+
const PHASE_MAP = {
|
|
29
|
+
proposed: "early",
|
|
30
|
+
accepted: "early",
|
|
31
|
+
defined: "early",
|
|
32
|
+
in_progress: "middle",
|
|
33
|
+
active: "middle",
|
|
34
|
+
experimental: "middle",
|
|
35
|
+
implemented: "late",
|
|
36
|
+
adopted: "late",
|
|
37
|
+
introduced: "late",
|
|
38
|
+
complete: "late",
|
|
39
|
+
consolidated: "late",
|
|
40
|
+
deprecated: "terminal",
|
|
41
|
+
retired: "terminal",
|
|
42
|
+
superseded: "terminal",
|
|
43
|
+
abandoned: "terminal",
|
|
44
|
+
deferred: "terminal",
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Determine the most advanced lifecycle state from lifecycle record.
|
|
48
|
+
* @param lifecycle - The lifecycle record to analyse.
|
|
49
|
+
* @returns The most advanced state name, or null if no active states.
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* getMostAdvancedState({ proposed: true, accepted: "2024-01-15" });
|
|
53
|
+
* // Returns "accepted"
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function getMostAdvancedState(lifecycle) {
|
|
57
|
+
if (!lifecycle)
|
|
58
|
+
return null;
|
|
59
|
+
// Find states that are true or have a date string
|
|
60
|
+
const activeStates = Object.entries(lifecycle)
|
|
61
|
+
.filter(([, value]) => value === true || typeof value === "string")
|
|
62
|
+
.map(([state]) => state);
|
|
63
|
+
if (activeStates.length === 0)
|
|
64
|
+
return null;
|
|
65
|
+
// Return the last active state (assumes ordered by progression)
|
|
66
|
+
return activeStates[activeStates.length - 1];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Infer lifecycle state for all nodes based on status and lifecycle fields.
|
|
70
|
+
*
|
|
71
|
+
* Returns inferred state, phase classification, and summary statistics.
|
|
72
|
+
*/
|
|
73
|
+
export const inferLifecycleOp = defineOperation({
|
|
74
|
+
name: "infer-lifecycle",
|
|
75
|
+
description: "Infer lifecycle state for nodes based on status and lifecycle fields",
|
|
76
|
+
input: z.object({
|
|
77
|
+
doc: SysProMDocument,
|
|
78
|
+
}),
|
|
79
|
+
output: LifecycleOutput,
|
|
80
|
+
fn: (input) => {
|
|
81
|
+
const results = [];
|
|
82
|
+
const summary = {
|
|
83
|
+
early: 0,
|
|
84
|
+
middle: 0,
|
|
85
|
+
late: 0,
|
|
86
|
+
terminal: 0,
|
|
87
|
+
unknown: 0,
|
|
88
|
+
};
|
|
89
|
+
for (const node of input.doc.nodes) {
|
|
90
|
+
// Determine inferred state from lifecycle or status
|
|
91
|
+
let inferredState;
|
|
92
|
+
let inferredPhase;
|
|
93
|
+
const lifecycleState = getMostAdvancedState(node.lifecycle);
|
|
94
|
+
if (lifecycleState) {
|
|
95
|
+
inferredState = lifecycleState;
|
|
96
|
+
inferredPhase = PHASE_MAP[lifecycleState] ?? "unknown";
|
|
97
|
+
}
|
|
98
|
+
else if (node.status) {
|
|
99
|
+
inferredState = node.status;
|
|
100
|
+
inferredPhase = PHASE_MAP[node.status] ?? "unknown";
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
inferredState = "undefined";
|
|
104
|
+
inferredPhase = "unknown";
|
|
105
|
+
}
|
|
106
|
+
summary[inferredPhase]++;
|
|
107
|
+
results.push({
|
|
108
|
+
id: node.id,
|
|
109
|
+
type: node.type,
|
|
110
|
+
name: node.name,
|
|
111
|
+
status: node.status,
|
|
112
|
+
lifecycle: node.lifecycle,
|
|
113
|
+
inferredState,
|
|
114
|
+
inferredPhase,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return { nodes: results, summary };
|
|
118
|
+
},
|
|
119
|
+
});
|