@intentius/chant-lexicon-aws 0.18.16 → 0.18.18

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.
@@ -2707,6 +2707,42 @@ export declare class Assessment {
2707
2707
  readonly CreationTime: number;
2708
2708
  }
2709
2709
 
2710
+ export declare class AssessmentFramework {
2711
+ constructor(props: {
2712
+ /** The control sets that are associated with the framework. */
2713
+ ControlSets: AssessmentFramework_ControlSet[];
2714
+ /** The name of the framework. */
2715
+ Name: string;
2716
+ /** The Amazon Resource Name (ARN) of the framework. */
2717
+ Arn?: string;
2718
+ /** The compliance type that the framework supports, such as CIS or HIPAA. */
2719
+ ComplianceType?: string;
2720
+ /** The time when the framework was created. */
2721
+ CreatedAt?: string;
2722
+ /** The user or role that created the framework. */
2723
+ CreatedBy?: string;
2724
+ /** The description of the framework. */
2725
+ Description?: string;
2726
+ /** The unique identifier for the framework. */
2727
+ FrameworkId?: string;
2728
+ /** The time when the framework was most recently updated. */
2729
+ LastUpdatedAt?: string;
2730
+ /** The user or role that most recently updated the framework. */
2731
+ LastUpdatedBy?: string;
2732
+ /** The tags associated with the framework. */
2733
+ Tags?: AssessmentFramework_Tag[];
2734
+ /** The framework type, such as a standard framework or a custom framework. */
2735
+ Type?: "Custom" | "Standard";
2736
+ }, attributes?: CFResourceAttributes);
2737
+ readonly Arn: string;
2738
+ readonly CreatedAt: string;
2739
+ readonly CreatedBy: string;
2740
+ readonly FrameworkId: string;
2741
+ readonly LastUpdatedAt: string;
2742
+ readonly LastUpdatedBy: string;
2743
+ readonly Type: "Custom" | "Standard";
2744
+ }
2745
+
2710
2746
  export declare class AssessmentTarget {
2711
2747
  constructor(props: {
2712
2748
  Arn?: string;
@@ -5684,6 +5720,20 @@ export declare class CodeDeployApplication {
5684
5720
  }, attributes?: CFResourceAttributes);
5685
5721
  }
5686
5722
 
5723
+ export declare class CodeInterpreter {
5724
+ constructor(props: {
5725
+ /** The Amazon Resource Name (ARN) of the code interpreter. */
5726
+ CodeInterpreterArn?: string;
5727
+ /** The unique identifier of the code interpreter. */
5728
+ CodeInterpreterId?: string;
5729
+ /** The current status of the code interpreter. */
5730
+ Status?: "CREATE_FAILED" | "CREATING" | "DELETED" | "DELETE_FAILED" | "DELETING" | "READY";
5731
+ }, attributes?: CFResourceAttributes);
5732
+ readonly CodeInterpreterArn: string;
5733
+ readonly CodeInterpreterId: string;
5734
+ readonly Status: "CREATE_FAILED" | "CREATING" | "DELETED" | "DELETE_FAILED" | "DELETING" | "READY";
5735
+ }
5736
+
5687
5737
  export declare class CodeInterpreterCustom {
5688
5738
  constructor(props: {
5689
5739
  /** The name of the code interpreter. */
@@ -13747,6 +13797,27 @@ export declare class EventType {
13747
13797
  readonly LastUpdatedTime: string;
13748
13798
  }
13749
13799
 
13800
+ export declare class EvidentlyExperiment {
13801
+ constructor(props: {
13802
+ MetricGoals: EvidentlyExperiment_MetricGoalObject[];
13803
+ Name: string;
13804
+ OnlineAbConfig: EvidentlyExperiment_OnlineAbConfigObject;
13805
+ Project: string;
13806
+ Treatments: EvidentlyExperiment_TreatmentObject[];
13807
+ Arn?: string;
13808
+ Description?: string;
13809
+ RandomizationSalt?: string;
13810
+ RemoveSegment?: boolean;
13811
+ /** Start Experiment. Default is False */
13812
+ RunningStatus?: EvidentlyExperiment_RunningStatusObject;
13813
+ SamplingRate?: number;
13814
+ Segment?: string;
13815
+ /** An array of key-value pairs to apply to this resource. */
13816
+ Tags?: EvidentlyExperiment_Tag[];
13817
+ }, attributes?: CFResourceAttributes);
13818
+ readonly Arn: string;
13819
+ }
13820
+
13750
13821
  export declare class EvidentlyProject {
13751
13822
  constructor(props: {
13752
13823
  Name: string;
@@ -13828,27 +13899,6 @@ export declare class ExecutionPlan {
13828
13899
  readonly Id: string;
13829
13900
  }
13830
13901
 
13831
- export declare class Experiment {
13832
- constructor(props: {
13833
- MetricGoals: Experiment_MetricGoalObject[];
13834
- Name: string;
13835
- OnlineAbConfig: Experiment_OnlineAbConfigObject;
13836
- Project: string;
13837
- Treatments: Experiment_TreatmentObject[];
13838
- Arn?: string;
13839
- Description?: string;
13840
- RandomizationSalt?: string;
13841
- RemoveSegment?: boolean;
13842
- /** Start Experiment. Default is False */
13843
- RunningStatus?: Experiment_RunningStatusObject;
13844
- SamplingRate?: number;
13845
- Segment?: string;
13846
- /** An array of key-value pairs to apply to this resource. */
13847
- Tags?: Experiment_Tag[];
13848
- }, attributes?: CFResourceAttributes);
13849
- readonly Arn: string;
13850
- }
13851
-
13852
13902
  export declare class ExperimentTemplate {
13853
13903
  constructor(props: {
13854
13904
  Description: string;
@@ -17601,14 +17651,14 @@ export declare class InstanceGroupConfig {
17601
17651
  JobFlowId: string;
17602
17652
  AutoScalingPolicy?: InstanceGroupConfig_AutoScalingPolicy;
17603
17653
  BidPrice?: string;
17604
- Configurations?: InstanceGroupConfig_Configuration[];
17654
+ Configurations?: InstanceGroupConfig_AppConfiguration[];
17605
17655
  CustomAmiId?: string;
17606
17656
  EbsConfiguration?: InstanceGroupConfig_EbsConfiguration;
17607
- Id?: string;
17657
+ InstanceGroupId?: string;
17608
17658
  Market?: string;
17609
17659
  Name?: string;
17610
17660
  }, attributes?: CFResourceAttributes);
17611
- readonly Id: string;
17661
+ readonly InstanceGroupId: string;
17612
17662
  }
17613
17663
 
17614
17664
  export declare class InstanceProfile {
@@ -30766,6 +30816,28 @@ export declare class SageMakerEndpoint {
30766
30816
  readonly EndpointName: string;
30767
30817
  }
30768
30818
 
30819
+ export declare class SageMakerExperiment {
30820
+ constructor(props: {
30821
+ /** The name of the experiment. Must be unique in your AWS account and is not case-sensitive. */
30822
+ ExperimentName: string;
30823
+ /** The Amazon Resource Name (ARN) of the experiment. */
30824
+ Arn?: string;
30825
+ /** When the experiment was created. */
30826
+ CreationTime?: string;
30827
+ /** The description of the experiment. */
30828
+ Description?: string;
30829
+ /** The name of the experiment as displayed. The name does not need to be unique. */
30830
+ DisplayName?: string;
30831
+ /** When the experiment was last modified. */
30832
+ LastModifiedTime?: string;
30833
+ /** A list of tags to associate with the experiment. */
30834
+ Tags?: Record<string, unknown>[];
30835
+ }, attributes?: CFResourceAttributes);
30836
+ readonly Arn: string;
30837
+ readonly CreationTime: string;
30838
+ readonly LastModifiedTime: string;
30839
+ }
30840
+
30769
30841
  export declare class SageMakerHub {
30770
30842
  constructor(props: {
30771
30843
  /** A description of the hub. */
@@ -41403,6 +41475,14 @@ export declare class AppConfigResourceObject {
41403
41475
  });
41404
41476
  }
41405
41477
 
41478
+ export declare class AppConfiguration {
41479
+ constructor(props: {
41480
+ Classification?: string;
41481
+ ConfigurationProperties?: Record<string, unknown>;
41482
+ Configurations?: InstanceGroupConfig_AppConfiguration[];
41483
+ });
41484
+ }
41485
+
41406
41486
  export declare class AppCookieStickinessPolicy {
41407
41487
  constructor(props: {
41408
41488
  /** The name of the application cookie used for stickiness. */
@@ -43713,6 +43793,31 @@ export declare class Assessment_Tag {
43713
43793
  });
43714
43794
  }
43715
43795
 
43796
+ export declare class AssessmentFramework_ControlSet {
43797
+ constructor(props: {
43798
+ /** The list of controls within the control set. */
43799
+ Controls: AssessmentFramework_ControlSetControl[];
43800
+ /** The name of the control set. */
43801
+ Name: string;
43802
+ });
43803
+ }
43804
+
43805
+ export declare class AssessmentFramework_ControlSetControl {
43806
+ constructor(props: {
43807
+ /** The unique identifier of the control. */
43808
+ Id: string;
43809
+ });
43810
+ }
43811
+
43812
+ export declare class AssessmentFramework_Tag {
43813
+ constructor(props: {
43814
+ /** The key name of the tag. */
43815
+ Key: string;
43816
+ /** The value for the tag. */
43817
+ Value: string;
43818
+ });
43819
+ }
43820
+
43716
43821
  export declare class AssessmentReportsDestination {
43717
43822
  constructor(props: {
43718
43823
  Destination?: string;
@@ -56326,6 +56431,22 @@ export declare class ControlPlaneScalingConfig {
56326
56431
  });
56327
56432
  }
56328
56433
 
56434
+ export declare class ControlSet {
56435
+ constructor(props: {
56436
+ /** The list of controls within the control set. */
56437
+ Controls: AssessmentFramework_ControlSetControl[];
56438
+ /** The name of the control set. */
56439
+ Name: string;
56440
+ });
56441
+ }
56442
+
56443
+ export declare class ControlSetControl {
56444
+ constructor(props: {
56445
+ /** The unique identifier of the control. */
56446
+ Id: string;
56447
+ });
56448
+ }
56449
+
56329
56450
  export declare class CookieObject {
56330
56451
  constructor(props: {
56331
56452
  /** The specific cookies to forward to your distribution's origin. */
@@ -69802,6 +69923,65 @@ export declare class EventType_Tag {
69802
69923
  });
69803
69924
  }
69804
69925
 
69926
+ export declare class EvidentlyExperiment_MetricGoalObject {
69927
+ constructor(props: {
69928
+ DesiredChange: "DECREASE" | "INCREASE";
69929
+ /** The JSON path to reference the entity id in the event. */
69930
+ EntityIdKey: string;
69931
+ MetricName: string;
69932
+ /** The JSON path to reference the numerical metric value in the event. */
69933
+ ValueKey: string;
69934
+ /** Event patterns have the same structure as the events they match. Rules use event patterns to select events. An event pattern either matches an event or it doesn't. */
69935
+ EventPattern?: string;
69936
+ UnitLabel?: string;
69937
+ });
69938
+ }
69939
+
69940
+ export declare class EvidentlyExperiment_OnlineAbConfigObject {
69941
+ constructor(props: {
69942
+ ControlTreatmentName?: string;
69943
+ TreatmentWeights?: EvidentlyExperiment_TreatmentToWeight[];
69944
+ });
69945
+ }
69946
+
69947
+ export declare class EvidentlyExperiment_RunningStatusObject {
69948
+ constructor(props: {
69949
+ /** Provide the analysis Completion time for an experiment */
69950
+ AnalysisCompleteTime?: string;
69951
+ /** Provide CANCELLED or COMPLETED desired state when stopping an experiment */
69952
+ DesiredState?: string;
69953
+ /** Reason is a required input for stopping the experiment */
69954
+ Reason?: string;
69955
+ /** Provide START or STOP action to apply on an experiment */
69956
+ Status?: string;
69957
+ });
69958
+ }
69959
+
69960
+ export declare class EvidentlyExperiment_Tag {
69961
+ constructor(props: {
69962
+ /** The key name of the tag. You can specify a value that is 1 to 128 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -. */
69963
+ Key: string;
69964
+ /** The value for the tag. You can specify a value that is 0 to 256 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -. */
69965
+ Value: string;
69966
+ });
69967
+ }
69968
+
69969
+ export declare class EvidentlyExperiment_TreatmentObject {
69970
+ constructor(props: {
69971
+ Feature: string;
69972
+ TreatmentName: string;
69973
+ Variation: string;
69974
+ Description?: string;
69975
+ });
69976
+ }
69977
+
69978
+ export declare class EvidentlyExperiment_TreatmentToWeight {
69979
+ constructor(props: {
69980
+ SplitWeight: number;
69981
+ Treatment: string;
69982
+ });
69983
+ }
69984
+
69805
69985
  export declare class EvidentlyProject_AppConfigResourceObject {
69806
69986
  constructor(props: {
69807
69987
  ApplicationId: string;
@@ -69971,65 +70151,6 @@ export declare class ExecutiveSummaryOption {
69971
70151
  });
69972
70152
  }
69973
70153
 
69974
- export declare class Experiment_MetricGoalObject {
69975
- constructor(props: {
69976
- DesiredChange: "DECREASE" | "INCREASE";
69977
- /** The JSON path to reference the entity id in the event. */
69978
- EntityIdKey: string;
69979
- MetricName: string;
69980
- /** The JSON path to reference the numerical metric value in the event. */
69981
- ValueKey: string;
69982
- /** Event patterns have the same structure as the events they match. Rules use event patterns to select events. An event pattern either matches an event or it doesn't. */
69983
- EventPattern?: string;
69984
- UnitLabel?: string;
69985
- });
69986
- }
69987
-
69988
- export declare class Experiment_OnlineAbConfigObject {
69989
- constructor(props: {
69990
- ControlTreatmentName?: string;
69991
- TreatmentWeights?: Experiment_TreatmentToWeight[];
69992
- });
69993
- }
69994
-
69995
- export declare class Experiment_RunningStatusObject {
69996
- constructor(props: {
69997
- /** Provide the analysis Completion time for an experiment */
69998
- AnalysisCompleteTime?: string;
69999
- /** Provide CANCELLED or COMPLETED desired state when stopping an experiment */
70000
- DesiredState?: string;
70001
- /** Reason is a required input for stopping the experiment */
70002
- Reason?: string;
70003
- /** Provide START or STOP action to apply on an experiment */
70004
- Status?: string;
70005
- });
70006
- }
70007
-
70008
- export declare class Experiment_Tag {
70009
- constructor(props: {
70010
- /** The key name of the tag. You can specify a value that is 1 to 128 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -. */
70011
- Key: string;
70012
- /** The value for the tag. You can specify a value that is 0 to 256 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -. */
70013
- Value: string;
70014
- });
70015
- }
70016
-
70017
- export declare class Experiment_TreatmentObject {
70018
- constructor(props: {
70019
- Feature: string;
70020
- TreatmentName: string;
70021
- Variation: string;
70022
- Description?: string;
70023
- });
70024
- }
70025
-
70026
- export declare class Experiment_TreatmentToWeight {
70027
- constructor(props: {
70028
- SplitWeight: number;
70029
- Treatment: string;
70030
- });
70031
- }
70032
-
70033
70154
  export declare class ExperimentConfig {
70034
70155
  constructor(props: {
70035
70156
  /** The name of an existing experiment to associate with the trial component. */
@@ -78935,24 +79056,26 @@ export declare class InstanceFleetConfig_SpotResizingSpecification {
78935
79056
  });
78936
79057
  }
78937
79058
 
78938
- export declare class InstanceGroupConfig_AutoScalingPolicy {
79059
+ export declare class InstanceGroupConfig_AppConfiguration {
78939
79060
  constructor(props: {
78940
- Constraints: InstanceGroupConfig_ScalingConstraints;
78941
- Rules: InstanceGroupConfig_ScalingRule[];
79061
+ Classification?: string;
79062
+ ConfigurationProperties?: Record<string, unknown>;
79063
+ Configurations?: InstanceGroupConfig_AppConfiguration[];
78942
79064
  });
78943
79065
  }
78944
79066
 
78945
- export declare class InstanceGroupConfig_Configuration {
79067
+ export declare class InstanceGroupConfig_AutoScalingPolicy {
78946
79068
  constructor(props: {
78947
- Classification?: string;
78948
- ConfigurationProperties?: Record<string, unknown>;
78949
- Configurations?: InstanceGroupConfig_Configuration[];
79069
+ Constraints: InstanceGroupConfig_ScalingConstraints;
79070
+ Rules: InstanceGroupConfig_ScalingRule[];
78950
79071
  });
78951
79072
  }
78952
79073
 
78953
79074
  export declare class InstanceGroupConfig_EbsBlockDeviceConfig {
78954
79075
  constructor(props: {
78955
79076
  VolumeSpecification: InstanceGroupConfig_VolumeSpecification;
79077
+ /** Use of this property can confuse CloudFormation drift detection. The EbsBlockDeviceConfigs read from the system may return a list with one entry per volume, replacing any entry specified in the template with a VolumesPerInstance greater than one by that many entries containing only the VolumeSpecification. Thus to avoid false drift detection, it is recommended to supply repeated entries in EbsBlockDeviceConfigs for any VolumeSpecification which is intended to be repeated and not to use this property.
79078
+ */
78956
79079
  VolumesPerInstance?: number;
78957
79080
  });
78958
79081
  }
@@ -85949,10 +86072,10 @@ export declare class LogAlarm_ScheduleConfiguration {
85949
86072
  constructor(props: {
85950
86073
  /** The expression that defines when the scheduled query runs, e.g. rate(1 minute). */
85951
86074
  ScheduleExpression: string;
85952
- /** The number of seconds into the past to end the query window. */
86075
+ /** The number of seconds into the past to start the query window. Must be a positive value and cannot exceed 2592000 seconds (30 days). */
86076
+ StartTimeOffset: number;
86077
+ /** The number of seconds into the past to end the query window. Must be a non-negative value and cannot exceed 2592000 seconds (30 days). */
85953
86078
  EndTimeOffset?: number;
85954
- /** The number of seconds into the past to start the query window. */
85955
- StartTimeOffset?: number;
85956
86079
  });
85957
86080
  }
85958
86081
 
@@ -85960,14 +86083,16 @@ export declare class LogAlarm_ScheduledQueryConfiguration {
85960
86083
  constructor(props: {
85961
86084
  /** The aggregation expression for the scheduled query, e.g. count(*) or avg(latency) by host. */
85962
86085
  AggregationExpression: string;
85963
- /** The log groups to query. */
85964
- LogGroupIdentifiers: string[];
85965
86086
  /** The query string to execute against the specified log groups. */
85966
86087
  QueryString: string;
85967
86088
  /** The schedule configuration. */
85968
86089
  ScheduleConfiguration: LogAlarm_ScheduleConfiguration;
85969
86090
  /** The ARN of the IAM role that grants permissions to execute the scheduled query. */
85970
86091
  ScheduledQueryRoleARN: string;
86092
+ /** The log groups to query. */
86093
+ LogGroupIdentifiers?: string[];
86094
+ /** A list of key-value pairs to associate with the scheduled query that backs the log alarm. */
86095
+ Tags?: LogAlarm_Tag[];
85971
86096
  });
85972
86097
  }
85973
86098
 
@@ -94660,7 +94785,7 @@ export declare class OnInput {
94660
94785
  export declare class OnlineAbConfigObject {
94661
94786
  constructor(props: {
94662
94787
  ControlTreatmentName?: string;
94663
- TreatmentWeights?: Experiment_TreatmentToWeight[];
94788
+ TreatmentWeights?: EvidentlyExperiment_TreatmentToWeight[];
94664
94789
  });
94665
94790
  }
94666
94791
 
@@ -109455,14 +109580,16 @@ export declare class ScheduledQueryConfiguration {
109455
109580
  constructor(props: {
109456
109581
  /** The aggregation expression for the scheduled query, e.g. count(*) or avg(latency) by host. */
109457
109582
  AggregationExpression: string;
109458
- /** The log groups to query. */
109459
- LogGroupIdentifiers: string[];
109460
109583
  /** The query string to execute against the specified log groups. */
109461
109584
  QueryString: string;
109462
109585
  /** The schedule configuration. */
109463
109586
  ScheduleConfiguration: LogAlarm_ScheduleConfiguration;
109464
109587
  /** The ARN of the IAM role that grants permissions to execute the scheduled query. */
109465
109588
  ScheduledQueryRoleARN: string;
109589
+ /** The log groups to query. */
109590
+ LogGroupIdentifiers?: string[];
109591
+ /** A list of key-value pairs to associate with the scheduled query that backs the log alarm. */
109592
+ Tags?: LogAlarm_Tag[];
109466
109593
  });
109467
109594
  }
109468
109595
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-aws",
3
- "version": "0.18.16",
3
+ "version": "0.18.18",
4
4
  "description": "AWS CloudFormation lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -80,7 +80,7 @@
80
80
  "typescript": "^5.9.3"
81
81
  },
82
82
  "peerDependencies": {
83
- "@intentius/chant": "^0.18.16",
83
+ "@intentius/chant": "^0.18.18",
84
84
  "typescript": "^5.9.3"
85
85
  }
86
86
  }
@@ -48,6 +48,57 @@ describe("cfn-deploy (#557)", () => {
48
48
  ).rejects.toThrow(/CREATE_FAILED/);
49
49
  });
50
50
 
51
+ it("throws fail-closed on an undefined stack status (Floci) instead of a TypeError (#947)", async () => {
52
+ const base = createMockCloudExecutor({ stacks: { "search-service": {} } });
53
+ const executor = {
54
+ ...base.executor,
55
+ cloudformation: {
56
+ ...base.executor.cloudformation,
57
+ waitForStack: async () => ({ stackStatus: undefined as unknown as string, outputs: {} }),
58
+ },
59
+ };
60
+ const capability = createCfnDeployCapability(executor);
61
+ await expect(
62
+ capability.run(ctx, { stack: "search-service", template: "archive:search.template.json" }),
63
+ ).rejects.toThrow(/indeterminate status/);
64
+ });
65
+
66
+ it("rollback degrades gracefully when the target lacks RollbackStack (Floci) (#947)", async () => {
67
+ const base = createMockCloudExecutor({ stacks: { "search-service": {} } });
68
+ const executor = {
69
+ ...base.executor,
70
+ cloudformation: {
71
+ ...base.executor.cloudformation,
72
+ rollbackStack: async () => {
73
+ throw new Error(
74
+ "An error occurred (UnknownAction) when calling the RollbackStack operation: Action RollbackStack is not supported.",
75
+ );
76
+ },
77
+ },
78
+ };
79
+ const capability = createCfnDeployCapability(executor);
80
+ await expect(
81
+ capability.rollback!(ctx, { stack: "search-service", template: "archive:search.template.json" }),
82
+ ).resolves.toBeUndefined();
83
+ });
84
+
85
+ it("rollback rethrows a genuine failure (not an unsupported action) (#947)", async () => {
86
+ const base = createMockCloudExecutor({ stacks: { "search-service": {} } });
87
+ const executor = {
88
+ ...base.executor,
89
+ cloudformation: {
90
+ ...base.executor.cloudformation,
91
+ rollbackStack: async () => {
92
+ throw new Error("An error occurred (AccessDenied): not authorized to RollbackStack");
93
+ },
94
+ },
95
+ };
96
+ const capability = createCfnDeployCapability(executor);
97
+ await expect(
98
+ capability.rollback!(ctx, { stack: "search-service", template: "archive:search.template.json" }),
99
+ ).rejects.toThrow(/AccessDenied/);
100
+ });
101
+
51
102
  it("passes wired inputs and imageRef through as CloudFormation parameters", async () => {
52
103
  const mock = createMockCloudExecutor({ stacks: { "search-service": {} } });
53
104
  const capability = createCfnDeployCapability(mock.executor);
@@ -153,15 +153,33 @@ export function createCfnDeployCapability(
153
153
  // failed deploy as green (and hand downstream steps an empty `outputs`),
154
154
  // so throw — the same fail-closed contract the rest of the release model
155
155
  // relies on.
156
- if (stackStatus.includes("ROLLBACK") || stackStatus.endsWith("_FAILED")) {
156
+ // A missing status is fail-closed too: Floci's describe-stacks can omit
157
+ // StackStatus mid-rollback, and `undefined.includes` would throw a TypeError
158
+ // that masks the real deploy failure (#947).
159
+ if (!stackStatus || stackStatus.includes("ROLLBACK") || stackStatus.endsWith("_FAILED")) {
157
160
  throw new Error(
158
- `cfn-deploy "${stack}": stack reached ${stackStatus} — the deploy failed and was rolled back. Inspect the stack events for the resource that failed to create/update.`,
161
+ `cfn-deploy "${stack}": stack reached ${stackStatus ?? "an indeterminate status"} — the deploy failed and was rolled back. Inspect the stack events for the resource that failed to create/update.`,
159
162
  );
160
163
  }
161
164
  return { stackStatus, outputs, ...(snapshotId ? { snapshotId } : {}) };
162
165
  },
163
166
  async rollback(ctx, input) {
164
- await executor.cloudformation.rollbackStack(input.stack ?? ctx.component);
167
+ const stack = input.stack ?? ctx.component;
168
+ try {
169
+ await executor.cloudformation.rollbackStack(stack);
170
+ } catch (err) {
171
+ const message = err instanceof Error ? err.message : String(err);
172
+ // Local emulators (Floci) don't implement RollbackStack → `UnknownAction`.
173
+ // The stack is already in a terminal failed state; degrade to a clear
174
+ // message instead of crashing the compensation phase (#947).
175
+ if (/UnknownAction|not supported/i.test(message)) {
176
+ console.error(
177
+ `cfn-deploy rollback "${stack}": the target doesn't support RollbackStack (a local emulator such as Floci) — skipping automated rollback. Original: ${message}`,
178
+ );
179
+ return;
180
+ }
181
+ throw err;
182
+ }
165
183
  },
166
184
  };
167
185
  }
@@ -1,5 +1,25 @@
1
1
  import { describe, test, expect } from "vitest";
2
- import { applyAwsEndpoint, applyAwsEndpointArgv } from "./cloud-executor";
2
+ import { applyAwsEndpoint, applyAwsEndpointArgv, ecsDeploymentId, ecsServiceStable } from "./cloud-executor";
3
+
4
+ describe("ecsDeploymentId / ecsServiceStable — tolerate Floci's missing deployments (#937)", () => {
5
+ test("deployment id: real AWS shape", () => {
6
+ expect(ecsDeploymentId({ service: { deployments: [{ id: "ecs-svc/123" }] } })).toBe("ecs-svc/123");
7
+ });
8
+ test("deployment id: Floci omits `deployments` entirely → '' (no crash)", () => {
9
+ expect(ecsDeploymentId({ service: {} })).toBe("");
10
+ expect(ecsDeploymentId({ service: { deployments: [] } })).toBe("");
11
+ });
12
+
13
+ test("stable: running==desired with ≤1 deployment", () => {
14
+ expect(ecsServiceStable({ runningCount: 2, desiredCount: 2, deployments: [{}] })).toBe(true);
15
+ expect(ecsServiceStable({ runningCount: 1, desiredCount: 2, deployments: [{}] })).toBe(false);
16
+ expect(ecsServiceStable({ runningCount: 2, desiredCount: 2, deployments: [{}, {}] })).toBe(false);
17
+ });
18
+ test("stable: Floci omits `deployments` → treated as 0, no crash", () => {
19
+ expect(ecsServiceStable({ runningCount: 2, desiredCount: 2 })).toBe(true);
20
+ expect(ecsServiceStable(undefined)).toBe(true); // 0 == 0
21
+ });
22
+ });
3
23
 
4
24
  describe("applyAwsEndpoint", () => {
5
25
  const url = "http://localhost:4566";
@@ -463,6 +463,23 @@ const realCloudFormation: CloudFormationClient = {
463
463
  },
464
464
  };
465
465
 
466
+ /** Deployment id from an ECS `update-service` response. Tolerant of Floci, whose
467
+ * `service` omits `deployments` entirely (real AWS always includes it) — guards
468
+ * the index, not just `.id` (#937). Exported for testing. */
469
+ export function ecsDeploymentId(described: { service: { deployments?: Array<{ id: string }> } }): string {
470
+ return described.service.deployments?.[0]?.id ?? "";
471
+ }
472
+
473
+ /** Whether an ECS service is steady (running == desired, ≤1 active deployment).
474
+ * Tolerant of Floci's missing `deployments` field. Exported for testing. */
475
+ export function ecsServiceStable(
476
+ svc: { runningCount?: number; desiredCount?: number; deployments?: unknown[] } | undefined,
477
+ ): boolean {
478
+ const running = svc?.runningCount ?? 0;
479
+ const desired = svc?.desiredCount ?? 0;
480
+ return running === desired && (svc?.deployments?.length ?? 0) <= 1;
481
+ }
482
+
466
483
  const realEcs: EcsClient = {
467
484
  async updateService(args) {
468
485
  const parts = [`aws ecs update-service`, `--cluster ${q(args.cluster)}`, `--service ${q(args.service)}`];
@@ -470,20 +487,18 @@ const realEcs: EcsClient = {
470
487
  if (args.desiredCount !== undefined) parts.push(`--desired-count ${args.desiredCount}`);
471
488
  if (args.forceNewDeployment) parts.push(`--force-new-deployment`);
472
489
  const { stdout } = await run(parts.join(" "));
473
- const described = JSON.parse(stdout) as { service: { deployments: Array<{ id: string }> } };
474
- return { deploymentId: described.service.deployments[0]?.id ?? "" };
490
+ const described = JSON.parse(stdout) as { service: { deployments?: Array<{ id: string }> } };
491
+ return { deploymentId: ecsDeploymentId(described) };
475
492
  },
476
493
  async describeService(cluster, service) {
477
494
  const { stdout } = await run(
478
495
  `aws ecs describe-services --cluster ${q(cluster)} --services ${q(service)}`,
479
496
  );
480
497
  const described = JSON.parse(stdout) as {
481
- services: Array<{ runningCount: number; desiredCount: number; deployments: unknown[] }>;
498
+ services: Array<{ runningCount: number; desiredCount: number; deployments?: unknown[] }>;
482
499
  };
483
500
  const svc = described.services[0];
484
- const running = svc?.runningCount ?? 0;
485
- const desired = svc?.desiredCount ?? 0;
486
- return { runningCount: running, desiredCount: desired, stable: running === desired && (svc?.deployments.length ?? 0) <= 1 };
501
+ return { runningCount: svc?.runningCount ?? 0, desiredCount: svc?.desiredCount ?? 0, stable: ecsServiceStable(svc) };
487
502
  },
488
503
  async rollbackService(args) {
489
504
  await realEcs.updateService(args);
@@ -7,6 +7,7 @@ import { AttrRef } from "@intentius/chant/attrref";
7
7
  // precedent for aws-lexicon integration-style tests.
8
8
  import { resolveAttrRefs } from "../../../../packages/core/src/discovery/resolve";
9
9
  import { AgentCoreAgent } from "./agentcore-agent";
10
+ import { Split, Ref } from "../intrinsics";
10
11
  import { awsSerializer } from "../serializer";
11
12
 
12
13
  const baseProps = {
@@ -104,6 +105,17 @@ describe("AgentCoreAgent", () => {
104
105
  );
105
106
  });
106
107
 
108
+ test("VPC mode accepts a cross-stack intrinsic (Fn::Split) — checks presence, not .length (#938)", () => {
109
+ // A cross-stack value (Split of a Parameter) is a truthy intrinsic with no
110
+ // `.length`; it WAS supplied, so the composite must not reject it.
111
+ const subnets = Split(",", Ref("SubnetIdsParam")) as unknown as string[];
112
+ const sgs = Split(",", Ref("SgIdsParam")) as unknown as string[];
113
+ const instance = AgentCoreAgent({ ...baseProps, networkMode: "VPC", vpcSubnetIds: subnets, vpcSecurityGroupIds: sgs });
114
+ const vpcConfigProps = ((instance.runtime as any).props.NetworkConfiguration as any).props.NetworkModeConfig.props;
115
+ expect(vpcConfigProps.Subnets).toBe(subnets);
116
+ expect(vpcConfigProps.SecurityGroups).toBe(sgs);
117
+ });
118
+
107
119
  test("throws when memoryEventExpiryDays is out of CFN bounds (3-365)", () => {
108
120
  expect(() => AgentCoreAgent({ ...baseProps, memoryEventExpiryDays: 1 })).toThrow(
109
121
  "AgentCoreAgent memoryEventExpiryDays must be between 3 and 365",