@intentius/chant-lexicon-aws 0.40.0 → 0.41.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.
@@ -125,9 +125,16 @@ describe("assertPinnedSpec", () => {
125
125
  expect(() => assertPinnedSpec(pinned, { pin, pinnedNames: names, env: {} })).not.toThrow();
126
126
  });
127
127
 
128
- test("refuses a drifted archive — generation does not proceed", () => {
129
- expect(() => assertPinnedSpec(archive("AWS::A::One", "AWS::B::Two"), { pin, pinnedNames: names, env: {} }))
130
- .toThrow(/upstream CloudFormation schema has moved/);
128
+ test("reports a drifted archive — generation proceeds (#1473)", () => {
129
+ // Was a throw. Enforcement moved to the release-time surface gate, so a
130
+ // moving upstream can no longer redden an unrelated PR.
131
+ const warnings: string[] = [];
132
+ expect(() =>
133
+ assertPinnedSpec(archive("AWS::A::One", "AWS::B::Two"), {
134
+ pin, pinnedNames: names, env: {}, warn: (m) => warnings.push(m),
135
+ }),
136
+ ).not.toThrow();
137
+ expect(warnings[0]).toMatch(/upstream CloudFormation schema has moved/);
131
138
  });
132
139
 
133
140
  test("the accept env proceeds and reports instead", () => {
@@ -196,16 +203,26 @@ describe("byte churn vs a moved resource set (#1473)", () => {
196
203
  expect(warnings[0]).toContain("surface.snapshot.json");
197
204
  });
198
205
 
199
- test("a type removed — still refuses", () => {
200
- const { threw } = capture(archive("AWS::S3::Bucket"));
201
- expect(threw?.message).toContain("Generation refuses");
202
- expect(threw?.message).toContain("removed");
206
+ test("a type removed — reports loudly, does not throw", () => {
207
+ // Enforcement is the release-time surface gate; a removed type always
208
+ // shows up there. Throwing here made every aws PR hostage to upstream.
209
+ const { warnings, threw } = capture(archive("AWS::S3::Bucket"));
210
+ expect(threw).toBeUndefined();
211
+ expect(warnings[0]).toContain("removed");
212
+ expect(warnings[0]).toContain("Generation refuses");
203
213
  });
204
214
 
205
- test("a type added — still refuses", () => {
206
- const { threw } = capture(archive("AWS::S3::Bucket", "AWS::IAM::Role", "AWS::SQS::Queue"));
207
- expect(threw?.message).toContain("Generation refuses");
208
- expect(threw?.message).toContain("added");
215
+ test("a type added — reports loudly, does not throw", () => {
216
+ const { warnings, threw } = capture(archive("AWS::S3::Bucket", "AWS::IAM::Role", "AWS::SQS::Queue"));
217
+ expect(threw).toBeUndefined();
218
+ expect(warnings[0]).toContain("added");
219
+ });
220
+
221
+ test("a moved type set is reported more urgently than byte churn", () => {
222
+ const edited = new Map(pinned);
223
+ edited.set("AWS::S3::Bucket", schema("AWS::S3::Bucket", "reworded"));
224
+ expect(capture(edited).warnings[0]).toContain("resource set is unchanged");
225
+ expect(capture(archive("AWS::S3::Bucket")).warnings[0]).not.toContain("resource set is unchanged");
209
226
  });
210
227
 
211
228
  test("an unchanged archive neither warns nor throws", () => {
@@ -214,15 +231,13 @@ describe("byte churn vs a moved resource set (#1473)", () => {
214
231
  expect(warnings).toEqual([]);
215
232
  });
216
233
 
217
- test("with no previous type set to compare, a mismatch stays fatal", () => {
218
- // `specDrift` reports empty added/removed when it cannot compare, which
219
- // would otherwise read as "no type moved" and downgrade every mismatch.
234
+ test("generation is never blocked by the pin, whatever moved", () => {
235
+ // The invariant that matters now: `generate` always completes. Whether the
236
+ // result may SHIP is decided by the surface gate in validate.
220
237
  const edited = new Map(pinned);
221
238
  edited.set("AWS::S3::Bucket", schema("AWS::S3::Bucket", "reworded"));
222
-
223
- expect(() =>
224
- assertPinnedSpec(edited, { pin, pinnedNames: new Set(), env: {}, warn: () => {} }),
225
- ).toThrow(/Generation refuses/);
239
+ expect(() => assertPinnedSpec(edited, { pin, pinnedNames: new Set(), env: {}, warn: () => {} })).not.toThrow();
240
+ expect(() => assertPinnedSpec(archive("AWS::X::Y"), { pin, pinnedNames: names, env: {}, warn: () => {} })).not.toThrow();
226
241
  });
227
242
 
228
243
  test("the accept env still short-circuits both cases", () => {
package/src/spec/pin.ts CHANGED
@@ -56,8 +56,8 @@ export interface SpecPin {
56
56
  * want, and paste the printed pin here in its own commit.
57
57
  */
58
58
  export const AWS_SPEC_PIN: SpecPin = {
59
- digest: "sha256:f2a0f4f2c0685116fa928a01d889b2aa2a03e4f02cd3257b124cbb184f7e533b",
60
- resources: 1650,
59
+ digest: "sha256:30bca5721afaaa891d17f528af4bc9b2fc5f707e34e5c6c78d3afeb6f0f259b8",
60
+ resources: 1651,
61
61
  accepted: "2026-08-04",
62
62
  };
63
63
 
@@ -235,13 +235,20 @@ export function assertPinnedSpec(
235
235
  // Guarded on actually HAVING a previous type set: `specDrift` reports empty
236
236
  // added/removed when it has nothing to compare against, which would
237
237
  // otherwise read as "no type moved" and downgrade every mismatch.
238
- const typeSetKnown = pinnedNames !== undefined && pinnedNames.size > 0;
239
- const typeSetMoved = drift.added.length > 0 || drift.removed.length > 0;
240
-
241
- if (typeSetKnown && !typeSetMoved) {
242
- warn(driftMessage(drift, pin, { fatal: false }));
243
- return;
244
- }
245
-
246
- throw new Error(driftMessage(drift, pin));
238
+ // chant #1473 the pin reports, it does not gate.
239
+ //
240
+ // Refusing here made every aws PR hostage to CloudFormation: the archive
241
+ // gains and edits types through the day, and `generate` runs on every CI
242
+ // job, so an unrelated change goes red the moment upstream moves. Both the
243
+ // 0.39.0 and 0.40.1 releases died this way, and a PR *accepting* the drift
244
+ // was itself refused by a newer drift that arrived while its CI queued.
245
+ //
246
+ // Enforcement lives where the consequence is: core's
247
+ // `validateLexiconArtifacts` compares the generated API against the reviewed
248
+ // `surface.snapshot.json` and, armed by CHANT_RELEASE_GATE, refuses to
249
+ // publish a surface nobody reviewed. A new or removed type always shows up
250
+ // there, so nothing is lost by reporting rather than throwing here — while a
251
+ // description edit, which changes the digest and no declaration, stops
252
+ // costing a red build.
253
+ warn(driftMessage(drift, pin, { fatal: drift.added.length > 0 || drift.removed.length > 0 }));
247
254
  }
@@ -1273,6 +1273,7 @@
1273
1273
  "AWS::Redshift::EventSubscription",
1274
1274
  "AWS::Redshift::Integration",
1275
1275
  "AWS::Redshift::ScheduledAction",
1276
+ "AWS::Redshift::SnapshotSchedule",
1276
1277
  "AWS::RedshiftServerless::Namespace",
1277
1278
  "AWS::RedshiftServerless::Snapshot",
1278
1279
  "AWS::RedshiftServerless::Workgroup",