@hyperscale0/udl 2.1.0 → 2.3.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/CHANGELOG.md +19 -0
- package/dist/effects.d.ts +3 -7
- package/dist/effects.d.ts.map +1 -1
- package/dist/effects.js +117 -21
- package/dist/effects.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/validation.d.ts +13 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +252 -56
- package/dist/validation.js.map +1 -1
- package/docs/README.md +3 -0
- package/docs/llms-full.txt +4 -1
- package/docs/llms.txt +1 -1
- package/docs/piece-plans.md +148 -0
- package/docs/reference/clauses.md +1 -1
- package/docs/reference/cli.md +1 -1
- package/docs/reference/diagnostics.md +1 -1
- package/package.json +2 -2
- package/src/effects.ts +159 -22
- package/src/index.ts +1 -0
- package/src/validation.ts +341 -62
package/src/validation.ts
CHANGED
|
@@ -19,11 +19,14 @@ import {
|
|
|
19
19
|
type UdlInstrument,
|
|
20
20
|
type UdlStep,
|
|
21
21
|
type UdlAction,
|
|
22
|
+
type UdlPieceStageStage,
|
|
22
23
|
} from "./schema.js";
|
|
23
24
|
import { fixedIsoDurationMs } from "./duration.js";
|
|
24
25
|
import {
|
|
25
26
|
analyzeInstrumentFinance,
|
|
26
27
|
financeAdmissionProblem,
|
|
28
|
+
type FinanceIssue,
|
|
29
|
+
type FinanceOptions,
|
|
27
30
|
} from "./finance.js";
|
|
28
31
|
import { UDL_LIMITS } from "./limits.js";
|
|
29
32
|
import {
|
|
@@ -1365,90 +1368,366 @@ function validateInstrument(
|
|
|
1365
1368
|
}
|
|
1366
1369
|
}
|
|
1367
1370
|
|
|
1368
|
-
|
|
1371
|
+
for (const finIssue of instrumentFinanceIssues(instrument, {
|
|
1372
|
+
plans: planResolution.plans,
|
|
1373
|
+
})) {
|
|
1374
|
+
add([...base, ...finIssue.path], finIssue.message, finIssue.code);
|
|
1375
|
+
}
|
|
1376
|
+
validateAggregates(instrument, base, instruments, references, add);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* The finance oracle over an instrument's resolved action plans, with paths
|
|
1381
|
+
* rooted at the instrument. A piece-plan instrument is unfolded over the
|
|
1382
|
+
* runtime's piece progress; any other instrument runs once per action plan
|
|
1383
|
+
* combination. Contract-side callers pass the UDL projection of a blueprint
|
|
1384
|
+
* definition so both boundaries prove the same machine.
|
|
1385
|
+
*/
|
|
1386
|
+
export function instrumentFinanceIssues(
|
|
1387
|
+
instrument: UdlInstrument,
|
|
1388
|
+
options: FinanceOptions & {
|
|
1389
|
+
readonly plans?: readonly ResolvedActionPlan[];
|
|
1390
|
+
} = {},
|
|
1391
|
+
): readonly FinanceIssue[] {
|
|
1392
|
+
const plans = options.plans ?? resolveUdlActionPlans(instrument).plans;
|
|
1393
|
+
const financeOptions: FinanceOptions =
|
|
1394
|
+
options.penaltyMayBeNonzero === undefined
|
|
1395
|
+
? {}
|
|
1396
|
+
: { penaltyMayBeNonzero: options.penaltyMayBeNonzero };
|
|
1397
|
+
const issues: FinanceIssue[] = [];
|
|
1398
|
+
const seen = new Set<string>();
|
|
1399
|
+
const add = (
|
|
1400
|
+
path: readonly PropertyKey[],
|
|
1401
|
+
message: string,
|
|
1402
|
+
code: UdlIssueCode,
|
|
1403
|
+
): void => {
|
|
1404
|
+
const key = `${code}:${path.join(".")}:${message}`;
|
|
1405
|
+
if (seen.has(key)) return;
|
|
1406
|
+
seen.add(key);
|
|
1407
|
+
issues.push({ code, message, path });
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
const expansion = instrument.piecePlan
|
|
1411
|
+
? expandPieceProgress(instrument, plans)
|
|
1412
|
+
: undefined;
|
|
1413
|
+
if (expansion === "bound") {
|
|
1414
|
+
add(
|
|
1415
|
+
["actions"],
|
|
1416
|
+
`piece progress expansion exceeds reachable variant bound of ${UDL_LIMITS.maxActionExpansion}`,
|
|
1417
|
+
"UDL2010",
|
|
1418
|
+
);
|
|
1419
|
+
return issues;
|
|
1420
|
+
}
|
|
1421
|
+
if (expansion) {
|
|
1422
|
+
for (const finIssue of analyzeInstrumentFinance(
|
|
1423
|
+
expansion.instrument,
|
|
1424
|
+
financeOptions,
|
|
1425
|
+
)) {
|
|
1426
|
+
const message = expansion.displayNames.reduce(
|
|
1427
|
+
(text, [expanded, display]) => text.replaceAll(expanded, display),
|
|
1428
|
+
finIssue.message,
|
|
1429
|
+
);
|
|
1430
|
+
add(originFinancePath(expansion, finIssue.path), message, finIssue.code);
|
|
1431
|
+
}
|
|
1432
|
+
return issues;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1369
1435
|
const actionsWithPlans = Object.keys(instrument.actions).filter((aName) =>
|
|
1370
|
-
|
|
1436
|
+
plans.some((p) => p.action === aName),
|
|
1371
1437
|
);
|
|
1372
|
-
|
|
1373
1438
|
let totalCombinations = 1;
|
|
1374
1439
|
const actionPlanMap: Record<string, ResolvedActionPlan[]> = {};
|
|
1375
1440
|
for (const aName of actionsWithPlans) {
|
|
1376
|
-
const actionPlans =
|
|
1441
|
+
const actionPlans = plans.filter((p) => p.action === aName);
|
|
1377
1442
|
actionPlanMap[aName] = actionPlans;
|
|
1378
1443
|
totalCombinations *= actionPlans.length;
|
|
1379
1444
|
}
|
|
1380
|
-
|
|
1381
1445
|
if (
|
|
1382
1446
|
actionsWithPlans.length > 0 &&
|
|
1383
1447
|
totalCombinations > UDL_LIMITS.maxActionExpansion
|
|
1384
1448
|
) {
|
|
1385
1449
|
add(
|
|
1386
|
-
[
|
|
1450
|
+
["actions"],
|
|
1387
1451
|
`variant expansion exceeds combination bound of ${UDL_LIMITS.maxActionExpansion} (${totalCombinations} combinations)`,
|
|
1388
1452
|
"UDL2010",
|
|
1389
1453
|
);
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
}
|
|
1454
|
+
return issues;
|
|
1455
|
+
}
|
|
1456
|
+
const generateCombos = (
|
|
1457
|
+
keys: string[],
|
|
1458
|
+
): Record<string, ResolvedActionPlan>[] => {
|
|
1459
|
+
if (keys.length === 0) return [{}];
|
|
1460
|
+
const [first, ...rest] = keys;
|
|
1461
|
+
const restCombos = generateCombos(rest);
|
|
1462
|
+
const result: Record<string, ResolvedActionPlan>[] = [];
|
|
1463
|
+
for (const plan of actionPlanMap[first!]!) {
|
|
1464
|
+
for (const combo of restCombos) {
|
|
1465
|
+
result.push({ ...combo, [first!]: plan });
|
|
1402
1466
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1467
|
+
}
|
|
1468
|
+
return result;
|
|
1469
|
+
};
|
|
1470
|
+
const combinations =
|
|
1471
|
+
actionsWithPlans.length > 0 ? generateCombos(actionsWithPlans) : [{}];
|
|
1472
|
+
for (const combo of combinations) {
|
|
1473
|
+
const expandedActions: Record<string, UdlAction> = {};
|
|
1474
|
+
for (const [aName, aDef] of Object.entries(instrument.actions)) {
|
|
1475
|
+
expandedActions[aName] = planExpandedAction(aDef, combo[aName]);
|
|
1476
|
+
}
|
|
1477
|
+
for (const finIssue of analyzeInstrumentFinance(
|
|
1478
|
+
{ ...instrument, actions: expandedActions },
|
|
1479
|
+
financeOptions,
|
|
1480
|
+
)) {
|
|
1481
|
+
add(finIssue.path, finIssue.message, finIssue.code);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
return issues;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
/**
|
|
1488
|
+
* Only an action that moves money through calls is replaced by its expanded
|
|
1489
|
+
* leaves. Authored moves and steps always reach the oracle.
|
|
1490
|
+
*/
|
|
1491
|
+
function planExpandedAction(
|
|
1492
|
+
definition: UdlAction,
|
|
1493
|
+
plan: ResolvedActionPlan | undefined,
|
|
1494
|
+
): UdlAction {
|
|
1495
|
+
if (!plan || (definition.calls?.length ?? 0) === 0) return definition;
|
|
1496
|
+
const steps: UdlStep[] = [];
|
|
1497
|
+
const moves: UdlMove[] = [];
|
|
1498
|
+
for (const leaf of plan.leaves) {
|
|
1499
|
+
if ("key" in leaf.step) {
|
|
1500
|
+
moves.push(leaf.step as UdlMove);
|
|
1501
|
+
} else {
|
|
1502
|
+
steps.push(leaf.step as UdlStep);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
return { ...definition, moves, steps };
|
|
1506
|
+
}
|
|
1405
1507
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1508
|
+
interface PieceProgress {
|
|
1509
|
+
readonly funded: readonly string[];
|
|
1510
|
+
readonly consumed: readonly string[];
|
|
1511
|
+
}
|
|
1409
1512
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1513
|
+
interface PieceProgressExpansion {
|
|
1514
|
+
readonly instrument: UdlInstrument;
|
|
1515
|
+
/** Expanded action name to display name, longest first. */
|
|
1516
|
+
readonly displayNames: readonly (readonly [string, string])[];
|
|
1517
|
+
readonly actionOrigins: ReadonlyMap<
|
|
1518
|
+
string,
|
|
1519
|
+
{
|
|
1520
|
+
readonly action: string;
|
|
1521
|
+
readonly leafOrigins?: readonly (readonly string[])[];
|
|
1522
|
+
}
|
|
1523
|
+
>;
|
|
1524
|
+
readonly stateOrigins: ReadonlyMap<string, string>;
|
|
1525
|
+
readonly originStates: readonly string[];
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function progressKey(progress: PieceProgress): string {
|
|
1529
|
+
return `${progress.funded.join(",")}|${progress.consumed.join(",")}`;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/** Mirrors eligiblePieces in the engine's piece-plan dispatcher. */
|
|
1533
|
+
function eligiblePieces(
|
|
1534
|
+
plan: NonNullable<UdlInstrument["piecePlan"]>,
|
|
1535
|
+
stage: UdlPieceStageStage,
|
|
1536
|
+
progress: PieceProgress,
|
|
1537
|
+
): readonly string[] {
|
|
1538
|
+
return plan[`${stage}_order`].filter((id) =>
|
|
1539
|
+
stage === "fund"
|
|
1540
|
+
? !progress.funded.includes(id)
|
|
1541
|
+
: progress.funded.includes(id) && !progress.consumed.includes(id),
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* Unfolds a piece-plan instrument over the runtime's piece progress so the
|
|
1547
|
+
* ordinary lifecycle oracle sees exactly the states the dispatcher admits: a
|
|
1548
|
+
* piece-stage action moves the next eligible piece of its stage order, the
|
|
1549
|
+
* lifecycle state is retained until the stage's last piece moves, funding
|
|
1550
|
+
* cannot resume once a piece has left escrow, and an action gated on a drained
|
|
1551
|
+
* account is closed while a funded piece is still held. Every expanded state is
|
|
1552
|
+
* one (lifecycle state, progress) pair; expanded action names carry the piece
|
|
1553
|
+
* and the source state so each has exactly one transition. A quote-commit pair
|
|
1554
|
+
* is not carried through the expansion.
|
|
1555
|
+
*/
|
|
1556
|
+
function expandPieceProgress(
|
|
1557
|
+
instrument: UdlInstrument,
|
|
1558
|
+
plans: readonly ResolvedActionPlan[],
|
|
1559
|
+
): PieceProgressExpansion | "bound" | undefined {
|
|
1560
|
+
const plan = instrument.piecePlan;
|
|
1561
|
+
if (!plan) return undefined;
|
|
1562
|
+
const stateName = (state: string, progress: PieceProgress): string =>
|
|
1563
|
+
`${state}#${progressKey(progress)}`;
|
|
1564
|
+
const initialProgress: PieceProgress = { funded: [], consumed: [] };
|
|
1565
|
+
const stateOrigins = new Map<string, string>();
|
|
1566
|
+
const actionOrigins = new Map<
|
|
1567
|
+
string,
|
|
1568
|
+
{ action: string; leafOrigins?: readonly (readonly string[])[] }
|
|
1569
|
+
>();
|
|
1570
|
+
const displayNames: [string, string][] = [];
|
|
1571
|
+
const actions: Record<string, UdlAction> = {};
|
|
1572
|
+
const transitions: Record<string, { from: string[]; to: string }> = {};
|
|
1573
|
+
const pending: { state: string; progress: PieceProgress }[] = [
|
|
1574
|
+
{ state: instrument.lifecycle.initial, progress: initialProgress },
|
|
1575
|
+
];
|
|
1576
|
+
stateOrigins.set(
|
|
1577
|
+
stateName(instrument.lifecycle.initial, initialProgress),
|
|
1578
|
+
instrument.lifecycle.initial,
|
|
1579
|
+
);
|
|
1580
|
+
|
|
1581
|
+
const visit = (state: string, progress: PieceProgress): void => {
|
|
1582
|
+
const name = stateName(state, progress);
|
|
1583
|
+
if (stateOrigins.has(name)) return;
|
|
1584
|
+
stateOrigins.set(name, state);
|
|
1585
|
+
pending.push({ state, progress });
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
while (pending.length > 0) {
|
|
1589
|
+
const { state, progress } = pending.shift()!;
|
|
1590
|
+
if (stateOrigins.size > UDL_LIMITS.maxActionExpansion) return "bound";
|
|
1591
|
+
const held = progress.funded.filter(
|
|
1592
|
+
(id) => !progress.consumed.includes(id),
|
|
1593
|
+
);
|
|
1594
|
+
for (const [actionName, transition] of Object.entries(
|
|
1595
|
+
instrument.lifecycle.transitions,
|
|
1596
|
+
)) {
|
|
1597
|
+
if (!transition.from.includes(state)) continue;
|
|
1598
|
+
const definition = instrument.actions[actionName];
|
|
1599
|
+
if (!definition) continue;
|
|
1600
|
+
if (definition.requiresDrainedAccount && held.length > 0) continue;
|
|
1601
|
+
const stage = definition.pieceStage;
|
|
1602
|
+
if (!stage) {
|
|
1603
|
+
const expanded = `${actionName}#${state}#${progressKey(progress)}`;
|
|
1604
|
+
const variant = plans.find(
|
|
1605
|
+
(candidate) => candidate.action === actionName,
|
|
1606
|
+
);
|
|
1607
|
+
actions[expanded] = planExpandedAction(definition, variant);
|
|
1608
|
+
transitions[expanded] = {
|
|
1609
|
+
from: [stateName(state, progress)],
|
|
1610
|
+
to: stateName(transition.to, progress),
|
|
1611
|
+
};
|
|
1612
|
+
actionOrigins.set(expanded, {
|
|
1613
|
+
action: actionName,
|
|
1614
|
+
...(variant && (definition.calls?.length ?? 0) > 0
|
|
1615
|
+
? { leafOrigins: variant.leaves.map((leaf) => leaf.originPath) }
|
|
1616
|
+
: {}),
|
|
1617
|
+
});
|
|
1618
|
+
displayNames.push([expanded, actionName]);
|
|
1619
|
+
visit(transition.to, progress);
|
|
1620
|
+
continue;
|
|
1437
1621
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1622
|
+
if (stage.plan !== plan.id) continue;
|
|
1623
|
+
if (stage.stage === "fund" && progress.consumed.length > 0) continue;
|
|
1624
|
+
const eligible = eligiblePieces(plan, stage.stage, progress);
|
|
1625
|
+
const pieceId = eligible[0];
|
|
1626
|
+
if (pieceId === undefined) continue;
|
|
1627
|
+
const variant = plans.find(
|
|
1628
|
+
(candidate) =>
|
|
1629
|
+
candidate.action === actionName && candidate.pieceId === pieceId,
|
|
1630
|
+
);
|
|
1631
|
+
if (!variant) continue;
|
|
1632
|
+
const next: PieceProgress =
|
|
1633
|
+
stage.stage === "fund"
|
|
1634
|
+
? {
|
|
1635
|
+
funded: [...progress.funded, pieceId],
|
|
1636
|
+
consumed: progress.consumed,
|
|
1637
|
+
}
|
|
1638
|
+
: {
|
|
1639
|
+
funded: progress.funded,
|
|
1640
|
+
consumed: [...progress.consumed, pieceId],
|
|
1641
|
+
};
|
|
1642
|
+
const stageComplete =
|
|
1643
|
+
eligiblePieces(plan, stage.stage, next).length === 0;
|
|
1644
|
+
const target = stageComplete ? transition.to : state;
|
|
1645
|
+
const expanded = `${actionName}@${pieceId}#${state}#${progressKey(progress)}`;
|
|
1646
|
+
actions[expanded] = planExpandedAction(definition, variant);
|
|
1647
|
+
transitions[expanded] = {
|
|
1648
|
+
from: [stateName(state, progress)],
|
|
1649
|
+
to: stateName(target, next),
|
|
1441
1650
|
};
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
}
|
|
1651
|
+
actionOrigins.set(expanded, {
|
|
1652
|
+
action: actionName,
|
|
1653
|
+
leafOrigins: variant.leaves.map((leaf) => leaf.originPath),
|
|
1654
|
+
});
|
|
1655
|
+
displayNames.push([expanded, `${actionName}[${pieceId}]`]);
|
|
1656
|
+
visit(target, next);
|
|
1449
1657
|
}
|
|
1450
1658
|
}
|
|
1451
|
-
|
|
1659
|
+
|
|
1660
|
+
for (const [actionName, definition] of Object.entries(instrument.actions)) {
|
|
1661
|
+
if (Object.hasOwn(instrument.lifecycle.transitions, actionName)) continue;
|
|
1662
|
+
actions[actionName] = planExpandedAction(
|
|
1663
|
+
definition,
|
|
1664
|
+
plans.find((candidate) => candidate.action === actionName),
|
|
1665
|
+
);
|
|
1666
|
+
actionOrigins.set(actionName, { action: actionName });
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
return {
|
|
1670
|
+
instrument: {
|
|
1671
|
+
...instrument,
|
|
1672
|
+
actions,
|
|
1673
|
+
lifecycle: {
|
|
1674
|
+
initial: stateName(instrument.lifecycle.initial, initialProgress),
|
|
1675
|
+
states: [...stateOrigins.keys()],
|
|
1676
|
+
transitions,
|
|
1677
|
+
},
|
|
1678
|
+
},
|
|
1679
|
+
displayNames: [
|
|
1680
|
+
...displayNames,
|
|
1681
|
+
...[...stateOrigins.entries()].map(
|
|
1682
|
+
([expanded, origin]): [string, string] => [expanded, origin],
|
|
1683
|
+
),
|
|
1684
|
+
].sort((left, right) => right[0].length - left[0].length),
|
|
1685
|
+
actionOrigins,
|
|
1686
|
+
stateOrigins,
|
|
1687
|
+
originStates: instrument.lifecycle.states,
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
function originFinancePath(
|
|
1692
|
+
expansion: PieceProgressExpansion,
|
|
1693
|
+
path: readonly PropertyKey[],
|
|
1694
|
+
): readonly PropertyKey[] {
|
|
1695
|
+
const [head, second, third, fourth, ...rest] = path;
|
|
1696
|
+
if (
|
|
1697
|
+
head === "lifecycle" &&
|
|
1698
|
+
second === "states" &&
|
|
1699
|
+
typeof third === "number"
|
|
1700
|
+
) {
|
|
1701
|
+
const expandedState = expansion.instrument.lifecycle.states[third];
|
|
1702
|
+
const origin =
|
|
1703
|
+
expandedState === undefined
|
|
1704
|
+
? undefined
|
|
1705
|
+
: expansion.stateOrigins.get(expandedState);
|
|
1706
|
+
const index =
|
|
1707
|
+
origin === undefined ? -1 : expansion.originStates.indexOf(origin);
|
|
1708
|
+
return index >= 0
|
|
1709
|
+
? ["lifecycle", "states", index]
|
|
1710
|
+
: ["lifecycle", "states"];
|
|
1711
|
+
}
|
|
1712
|
+
if (head === "actions" && typeof second === "string") {
|
|
1713
|
+
const origin = expansion.actionOrigins.get(second);
|
|
1714
|
+
if (!origin) return path;
|
|
1715
|
+
if (
|
|
1716
|
+
third === "moves" &&
|
|
1717
|
+
typeof fourth === "number" &&
|
|
1718
|
+
origin.leafOrigins?.[fourth]
|
|
1719
|
+
) {
|
|
1720
|
+
return ["actions", ...origin.leafOrigins[fourth], ...rest];
|
|
1721
|
+
}
|
|
1722
|
+
return [
|
|
1723
|
+
"actions",
|
|
1724
|
+
origin.action,
|
|
1725
|
+
...(third === undefined ? [] : [third]),
|
|
1726
|
+
...(fourth === undefined ? [] : [fourth]),
|
|
1727
|
+
...rest,
|
|
1728
|
+
];
|
|
1729
|
+
}
|
|
1730
|
+
return path;
|
|
1452
1731
|
}
|
|
1453
1732
|
|
|
1454
1733
|
function validatePiecePlan(
|
|
@@ -4455,7 +4734,7 @@ function referencedPathDeclared(target: UdlInstrument, path: string): boolean {
|
|
|
4455
4734
|
function validateStep(
|
|
4456
4735
|
instrument: UdlInstrument,
|
|
4457
4736
|
action: UdlAction,
|
|
4458
|
-
step:
|
|
4737
|
+
step: ResolvedActionPlan["leaves"][number]["step"],
|
|
4459
4738
|
base: readonly PropertyKey[],
|
|
4460
4739
|
add: AddIssue,
|
|
4461
4740
|
): void {
|