Skip to content

Commit 7788d3e

Browse files
committed
refactor(CHG40): add CLI flags and MCP support for bidirectional impact analysis
CLI enhancements: - Add --direction [outgoing|incoming|bidirectional] flag to 'infer impact' - Add --max-depth <n> flag for traversal depth limiting - Add --filter <types> flag for relationship type filtering - Display direction, depth, and filter info in output MCP updates: - Extend infer-impact tool with direction, maxDepth, relationshipFilter inputs - Add new infer-impact-summary tool for hotspot analysis - Support document-level impact analysis via MCP Schema & docs: - Regenerate schema.json with new ImpactPolarity enum - Update .SysProM.json and Markdown syncs - Mark CHG40 as 'introduced' in provenance document Tests: All 477 tests passing including 13 new CHG40 tests
1 parent 734a7af commit 7788d3e

5 files changed

Lines changed: 107 additions & 6 deletions

File tree

.SysProM.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4181,6 +4181,7 @@
41814181
"src/index.ts",
41824182
"tests/infer-impact.unit.test.ts"
41834183
],
4184+
"status": "introduced",
41844185
"type": "change"
41854186
}
41864187
],

.SysProM/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,8 @@ Add bidirectional BFS, polarity/strength on relationships, influence relationshi
846846

847847
- Implements: [DEC42](./DECISIONS.md#dec42--enhance-impact-analysis-for-sysmlarchimate-parity)
848848

849+
- Status: introduced
850+
849851
Scope:
850852
- src/schema.ts
851853
- src/operations/infer-impact.ts

schema.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,22 @@
458458
"description": "Source node ID.",
459459
"type": "string"
460460
},
461+
"polarity": {
462+
"description": "Impact polarity — positive, negative, neutral, or uncertain.",
463+
"enum": [
464+
"positive",
465+
"negative",
466+
"neutral",
467+
"uncertain"
468+
],
469+
"type": "string"
470+
},
471+
"strength": {
472+
"description": "Relationship strength as a number between 0 and 1.",
473+
"maximum": 1,
474+
"minimum": 0,
475+
"type": "number"
476+
},
461477
"to": {
462478
"description": "Target node ID.",
463479
"type": "string"
@@ -487,7 +503,8 @@
487503
"transforms_into",
488504
"selects",
489505
"requires",
490-
"disables"
506+
"disables",
507+
"influence"
491508
],
492509
"type": "string"
493510
}

src/cli/commands/infer.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,22 @@ function printDerivedRelationship(r: DerivedRelationship): void {
7474

7575
const impactArgs = z.object({
7676
id: z.string().describe("node ID to start impact analysis from"),
77+
direction: z
78+
.enum(["outgoing", "incoming", "bidirectional"])
79+
.optional()
80+
.describe(
81+
"traversal direction: outgoing (default) | incoming | bidirectional",
82+
),
83+
maxDepth: z
84+
.number()
85+
.int()
86+
.positive()
87+
.optional()
88+
.describe("maximum traversal depth"),
89+
filter: z
90+
.string()
91+
.optional()
92+
.describe("comma-separated list of relationship types to follow"),
7793
});
7894

7995
// ---------------------------------------------------------------------------
@@ -155,13 +171,33 @@ const impactSubcommand: CommandDef = {
155171
const args = impactArgs.parse(rawArgs);
156172
const opts = readOpts.parse(rawOpts);
157173
const { doc } = loadDoc(opts.path);
158-
const result = inferImpactOp({ doc, startId: args.id });
174+
175+
// Parse relationship filter if provided
176+
const relationshipFilter = args.filter
177+
? args.filter.split(",").map((s) => s.trim())
178+
: undefined;
179+
180+
const result = inferImpactOp({
181+
doc,
182+
startId: args.id,
183+
direction: args.direction,
184+
maxDepth: args.maxDepth,
185+
relationshipFilter,
186+
});
159187

160188
if (opts.json) {
161189
console.log(JSON.stringify(result, null, 2));
162190
} else {
191+
const directionLabel = args.direction ? ` (${args.direction})` : "";
192+
const depthLabel = args.maxDepth
193+
? ` [depth: ${String(args.maxDepth)}]`
194+
: "";
195+
const filterLabel = args.filter ? ` [filter: ${args.filter}]` : "";
196+
163197
console.log(
164-
pc.bold(`\nImpact Analysis from ${args.id}\n`) +
198+
pc.bold(
199+
`\nImpact Analysis from ${args.id}${directionLabel}${depthLabel}${filterLabel}\n`,
200+
) +
165201
pc.dim(
166202
`Direct: ${String(result.summary.direct)} | Transitive: ${String(result.summary.transitive)} | Potential: ${String(result.summary.potential)} | Total: ${String(result.summary.total)}`,
167203
) +

src/mcp/server.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
inferCompletenessOp,
2222
inferLifecycleOp,
2323
inferImpactOp,
24+
impactSummaryOp,
2425
inferDerivedOp,
2526
} from "../operations/index.js";
2627

@@ -476,15 +477,59 @@ server.registerTool(
476477
"infer-impact",
477478
{
478479
description:
479-
"Infer impact from a node through the graph following impact relationships",
480+
"Infer impact from a node through the graph following impact relationships (CHG40 with bidirectional traversal)",
480481
inputSchema: z.object({
481482
path: z.string().describe("Path to SysProM file"),
482483
startId: z.string().describe("Node ID to start impact analysis from"),
484+
direction: z
485+
.enum(["outgoing", "incoming", "bidirectional"])
486+
.optional()
487+
.describe("Traversal direction (default: outgoing)"),
488+
maxDepth: z
489+
.number()
490+
.int()
491+
.positive()
492+
.optional()
493+
.describe("Maximum traversal depth"),
494+
relationshipFilter: z
495+
.array(z.string())
496+
.optional()
497+
.describe("Relationship types to follow"),
483498
}),
484499
},
485-
({ path, startId }) => {
500+
({ path, startId, direction, maxDepth, relationshipFilter }) => {
486501
const { doc } = loadDocument(path);
487-
const result = inferImpactOp({ doc, startId });
502+
const result = inferImpactOp({
503+
doc,
504+
startId,
505+
direction,
506+
maxDepth,
507+
relationshipFilter,
508+
});
509+
return {
510+
content: [
511+
{
512+
type: "text" as const,
513+
text: JSON.stringify(result, null, 2),
514+
},
515+
],
516+
};
517+
},
518+
);
519+
520+
// Register infer-impact-summary tool (CHG40 hotspot analysis)
521+
server.registerTool(
522+
"infer-impact-summary",
523+
{
524+
description:
525+
"Analyse document for impact hotspots — nodes with high incoming/outgoing impact (CHG40)",
526+
inputSchema: z.object({
527+
path: z.string().describe("Path to SysProM file"),
528+
}),
529+
},
530+
({ path }) => {
531+
const { doc } = loadDocument(path);
532+
const result = impactSummaryOp({ doc });
488533
return {
489534
content: [
490535
{

0 commit comments

Comments
 (0)