@intentius/chant 0.18.1 → 0.18.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/lifecycle.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAoBlD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAwE/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAoC3E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAmC3E;AA0OD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6G3E;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1E;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9E;AAkBD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C/E"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/lifecycle.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAoBlD;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAwE/E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAoC3E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C3E;AA2SD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6G3E;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1E;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9E;AAkBD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C/E"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAKA,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAiB9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAuLpD"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAKA,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAiB9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA2LpD"}
@@ -60,6 +60,10 @@ export interface ParsedArgs {
60
60
  /** `chant graph --live --overlay` — classify the provisioned graph against
61
61
  * declared source: managed / foreign / pending (#780). */
62
62
  overlay?: boolean;
63
+ /** `chant lifecycle diff <env> --between <refA> <refB>` (#822) — diff two saved
64
+ * snapshots (orphan-branch commits from `lifecycle log`) against each other. */
65
+ betweenA?: string;
66
+ betweenB?: string;
63
67
  /** `chant graph --live --overlay --overlay-anchor <source|live>` (#821) — which
64
68
  * graph is the canvas. `source` (default) keeps the declared edges (the
65
69
  * cross-substrate topology) and joins live status per node; `live` keeps the
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/discovery/collect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAM9D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GACjE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAoEzB"}
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/discovery/collect.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAkB9D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GACjE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAqEzB"}
@@ -46,6 +46,15 @@ export declare function writeSnapshot(environment: string, lexicon: string, json
46
46
  export declare function readSnapshot(environment: string, lexicon: string, opts?: {
47
47
  cwd?: string;
48
48
  }): Promise<string | null>;
49
+ /**
50
+ * Read a snapshot at a specific orphan-branch commit (#822). `readSnapshot` reads
51
+ * the branch tip; this reads `<ref>:<env>/<lexicon>.json` for any commit `ref`
52
+ * (as listed by {@link listSnapshots}), so two historical snapshots can be diffed.
53
+ * Returns null if that env/lexicon wasn't captured at `ref`.
54
+ */
55
+ export declare function readSnapshotAt(environment: string, lexicon: string, ref: string, opts?: {
56
+ cwd?: string;
57
+ }): Promise<string | null>;
49
58
  /**
50
59
  * Append one immutable release record line to `<environment>/releases.jsonl`
51
60
  * on the orphan branch (#568, epic #551 "Build & deploy observability"). Same
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/lifecycle/git.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/lifecycle/git.ts"],"names":[],"mappings":"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"}
@@ -46,6 +46,24 @@ export interface DiffLiveInput {
46
46
  /** Resources captured by the previous snapshot, if any. */
47
47
  observedThen: Record<string, ResourceMetadata> | undefined;
48
48
  }
49
+ /** The delta between two saved snapshots (#822): a two-way observed diff. */
50
+ export interface SnapshotDiffResult {
51
+ /** In `next`, not in `prev`. */
52
+ added: string[];
53
+ /** In `prev`, not in `next`. */
54
+ removed: string[];
55
+ /** In both; metadata differs (with the attribute-level changes). */
56
+ changed: ResourceDrift[];
57
+ /** In both; metadata identical. */
58
+ unchanged: string[];
59
+ }
60
+ /**
61
+ * Diff two **observed** snapshots (#822) — `prev` vs `next`, each a resources map
62
+ * read from the orphan branch. A two-way diff (no "declared" axis), so it answers
63
+ * "what changed in the cloud between two points" independent of source. Pure and
64
+ * deterministic (results sorted). Feeds the deployment-lanes frame-pair diff.
65
+ */
66
+ export declare function diffSnapshots(prev: Record<string, ResourceMetadata>, next: Record<string, ResourceMetadata>): SnapshotDiffResult;
49
67
  export declare function diffLive(input: DiffLiveInput): LiveDiffResult;
50
68
  export interface LiveArtifactDiffResult {
51
69
  /** Observed now, not in previous snapshot. */
@@ -1 +1 @@
1
- {"version":3,"file":"live-diff.d.ts","sourceRoot":"","sources":["../../src/lifecycle/live-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,qDAAqD;IACrD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mDAAmD;IACnD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mEAAmE;IACnE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,oBAAoB,EAAE,aAAa,EAAE,CAAC;IACtC,sDAAsD;IACtD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,CAAC;CAC5D;AAyCD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAiE7D;AAID,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,mCAAmC;IACnC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,sBAAsB,CAmCvF"}
1
+ {"version":3,"file":"live-diff.d.ts","sourceRoot":"","sources":["../../src/lifecycle/live-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,qDAAqD;IACrD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mDAAmD;IACnD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mEAAmE;IACnE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,oDAAoD;IACpD,oBAAoB,EAAE,aAAa,EAAE,CAAC;IACtC,sDAAsD;IACtD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,CAAC;CAC5D;AAyCD,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,gCAAgC;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gCAAgC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oEAAoE;IACpE,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,mCAAmC;IACnC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACrC,kBAAkB,CAiBpB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAiE7D;AAID,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iCAAiC;IACjC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,mCAAmC;IACnC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,sBAAsB,CAmCvF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -1,9 +1,9 @@
1
1
  import { resolve } from "node:path";
2
2
  import { build } from "../../build";
3
3
  import { takeSnapshot } from "../../lifecycle/snapshot";
4
- import { readSnapshot, readEnvironmentSnapshots, listSnapshots, fetchLifecycle, StaleLifecycleBranchError } from "../../lifecycle/git";
4
+ import { readSnapshot, readSnapshotAt, readEnvironmentSnapshots, listSnapshots, fetchLifecycle, StaleLifecycleBranchError } from "../../lifecycle/git";
5
5
  import { computeBuildDigest, diffDigests } from "../../lifecycle/digest";
6
- import { diffLive, diffLiveArtifacts, type LiveDiffResult, type LiveArtifactDiffResult } from "../../lifecycle/live-diff";
6
+ import { diffLive, diffLiveArtifacts, diffSnapshots, type LiveDiffResult, type LiveArtifactDiffResult, type SnapshotDiffResult } from "../../lifecycle/live-diff";
7
7
  import { buildChangeSet, renderChangeSet, gitlabMrReport, type ChangeSet } from "../../lifecycle/change-set";
8
8
  import { affectedStacks } from "../../lifecycle/affected";
9
9
  import { loadChantConfig } from "../../config";
@@ -159,6 +159,18 @@ export async function runLifecycleDiff(ctx: CommandContext): Promise<number> {
159
159
  return 1;
160
160
  }
161
161
 
162
+ // `--between <refA> <refB>` (#822): diff two saved snapshots against each other.
163
+ // Purely historical — no build, no cloud query; reads the orphan branch.
164
+ if (args.betweenA && args.betweenB) {
165
+ return runLifecycleDiffBetween({
166
+ environment,
167
+ lexiconFilter,
168
+ refA: args.betweenA,
169
+ refB: args.betweenB,
170
+ json: !!args.json,
171
+ });
172
+ }
173
+
162
174
  // Filter serializers to target lexicon before building
163
175
  const targetSerializers = lexiconFilter
164
176
  ? plugins.filter((p) => p.name === lexiconFilter).map((p) => p.serializer)
@@ -186,6 +198,71 @@ export async function runLifecycleDiff(ctx: CommandContext): Promise<number> {
186
198
  return runLifecycleDiffDigest({ environment, lexicons, buildResult });
187
199
  }
188
200
 
201
+ interface BetweenDiffArgs {
202
+ environment: string;
203
+ lexiconFilter?: string;
204
+ refA: string;
205
+ refB: string;
206
+ json: boolean;
207
+ }
208
+
209
+ /**
210
+ * `chant lifecycle diff <env> --between <refA> <refB>` (#822). Diffs two saved
211
+ * snapshots against each other — read-only, no build, no cloud query. Reads
212
+ * `<ref>:<env>/<lexicon>.json` from the orphan branch at each ref and reports the
213
+ * two-way delta per lexicon.
214
+ */
215
+ async function runLifecycleDiffBetween(args: BetweenDiffArgs): Promise<number> {
216
+ await fetchLifecycle();
217
+ const { config } = await loadChantConfig(resolve("."));
218
+ const lexicons = args.lexiconFilter ? [args.lexiconFilter] : config.lexicons ?? [];
219
+ if (lexicons.length === 0) {
220
+ console.error(formatError({ message: "No lexicons to diff — pass a lexicon or declare `lexicons` in chant.config." }));
221
+ return 1;
222
+ }
223
+
224
+ const parseResources = (content: string | null): Record<string, ResourceMetadata> | null => {
225
+ if (content === null) return null;
226
+ try {
227
+ return (JSON.parse(content) as LifecycleSnapshot).resources ?? {};
228
+ } catch {
229
+ return null;
230
+ }
231
+ };
232
+
233
+ const byLexicon: Record<string, SnapshotDiffResult> = {};
234
+ for (const lexicon of lexicons) {
235
+ const prev = parseResources(await readSnapshotAt(args.environment, lexicon, args.refA));
236
+ const next = parseResources(await readSnapshotAt(args.environment, lexicon, args.refB));
237
+ if (prev === null && next === null) {
238
+ if (!args.json) console.error(formatWarning({ message: `${lexicon}: not captured at either ref — skipping` }));
239
+ continue;
240
+ }
241
+ byLexicon[lexicon] = diffSnapshots(prev ?? {}, next ?? {});
242
+ }
243
+
244
+ if (args.json) {
245
+ console.log(JSON.stringify({ environment: args.environment, refA: args.refA, refB: args.refB, lexicons: byLexicon }, null, 2));
246
+ return 0;
247
+ }
248
+
249
+ if (Object.keys(byLexicon).length === 0) {
250
+ console.log(`No snapshots for ${args.environment} at ${args.refA} / ${args.refB}.`);
251
+ return 0;
252
+ }
253
+
254
+ console.log(formatBold(`Snapshot diff — ${args.environment}: ${args.refA} → ${args.refB}`));
255
+ for (const [lexicon, d] of Object.entries(byLexicon)) {
256
+ console.log(
257
+ `\n${formatBold(lexicon)} — ${d.added.length} added, ${d.removed.length} removed, ${d.changed.length} changed, ${d.unchanged.length} unchanged`,
258
+ );
259
+ for (const name of d.added) console.log(` + ${name}`);
260
+ for (const name of d.removed) console.log(` - ${name}`);
261
+ for (const drift of d.changed) console.log(` ~ ${drift.name} (${drift.changes.map((c) => c.path).join(", ")})`);
262
+ }
263
+ return 0;
264
+ }
265
+
189
266
  interface DigestDiffArgs {
190
267
  environment: string;
191
268
  lexicons: string[];
package/src/cli/main.ts CHANGED
@@ -94,6 +94,10 @@ export function parseArgs(args: string[]): ParsedArgs {
94
94
  result.live = true;
95
95
  } else if (arg === "--overlay") {
96
96
  result.overlay = true;
97
+ } else if (arg === "--between") {
98
+ result.betweenA = args[++i];
99
+ result.betweenB = args[++i];
100
+ if (!result.betweenA || !result.betweenB) throw new Error("--between needs two snapshot refs: --between <refA> <refB>");
97
101
  } else if (arg === "--overlay-anchor") {
98
102
  const v = args[++i];
99
103
  if (v !== "source" && v !== "live") throw new Error(`--overlay-anchor must be 'source' or 'live', got '${v}'`);
@@ -61,6 +61,10 @@ export interface ParsedArgs {
61
61
  /** `chant graph --live --overlay` — classify the provisioned graph against
62
62
  * declared source: managed / foreign / pending (#780). */
63
63
  overlay?: boolean;
64
+ /** `chant lifecycle diff <env> --between <refA> <refB>` (#822) — diff two saved
65
+ * snapshots (orphan-branch commits from `lifecycle log`) against each other. */
66
+ betweenA?: string;
67
+ betweenB?: string;
64
68
  /** `chant graph --live --overlay --overlay-anchor <source|live>` (#821) — which
65
69
  * graph is the canvas. `source` (default) keeps the declared edges (the
66
70
  * cross-substrate topology) and joins live status per node; `live` keeps the
@@ -301,3 +301,25 @@ describe("collectEntities with composites", () => {
301
301
  expect(entities.has("myComp")).toBe(false);
302
302
  });
303
303
  });
304
+
305
+ describe("collectEntities — default exports (op files)", () => {
306
+ test("two files with `export default` do not collide (keyed per file)", () => {
307
+ const apply = createMockEntity("Temporal::Op");
308
+ const reconcile = createMockEntity("Temporal::Op");
309
+ const result = collectEntities([
310
+ { file: "ops/apply.op.ts", exports: { default: apply } },
311
+ { file: "ops/reconcile.op.ts", exports: { default: reconcile } },
312
+ ]);
313
+ expect(result.size).toBe(2);
314
+ // keyed by file basename (sans .op.ts), not the literal "default"
315
+ expect(result.get("apply")).toBe(apply);
316
+ expect(result.get("reconcile")).toBe(reconcile);
317
+ expect(result.has("default")).toBe(false);
318
+ });
319
+
320
+ test("a single default export is collected (not dropped)", () => {
321
+ const op = createMockEntity("Temporal::Op");
322
+ const result = collectEntities([{ file: "ops/deploy.op.ts", exports: { default: op } }]);
323
+ expect(result.get("deploy")).toBe(op);
324
+ });
325
+ });
@@ -1,9 +1,22 @@
1
+ import { basename } from "node:path";
1
2
  import { isDeclarable, type Declarable } from "../declarable";
2
3
  import { isCompositeInstance, expandComposite } from "../composite";
3
4
  import { isLexiconOutput } from "../lexicon-output";
4
5
  import { DiscoveryError } from "../errors";
5
6
  import { setProvenance } from "../provenance";
6
7
 
8
+ /**
9
+ * The entity key for an export. `export default` is per-module — the `Op` pattern
10
+ * (`export default op`) uses it, and two op files must not collide on the literal
11
+ * name "default". Key a default export by the file's basename so distinct files
12
+ * stay distinct; named exports keep their name. (Serializers that care about the
13
+ * declared name — e.g. the Op serializer — read it from the entity, not this key.)
14
+ */
15
+ function exportKey(rawName: string, file: string): string {
16
+ if (rawName !== "default") return rawName;
17
+ return basename(file).replace(/\.ts$/, "").replace(/\.op$/, "");
18
+ }
19
+
7
20
  /**
8
21
  * Collects all declarable entities from imported modules.
9
22
  * CompositeInstance exports are expanded into individual entities
@@ -21,7 +34,8 @@ export function collectEntities(
21
34
  const entities = new Map<string, Declarable>();
22
35
 
23
36
  for (const { file, exports } of modules) {
24
- for (const [name, value] of Object.entries(exports)) {
37
+ for (const [rawName, value] of Object.entries(exports)) {
38
+ const name = exportKey(rawName, file);
25
39
  if (isDeclarable(value)) {
26
40
  if (entities.has(name)) {
27
41
  // Same object re-exported from multiple files (e.g. re-exports from multiple files) is fine
@@ -6,6 +6,7 @@ import { join } from "node:path";
6
6
  import {
7
7
  writeSnapshot,
8
8
  readSnapshot,
9
+ readSnapshotAt,
9
10
  readEnvironmentSnapshots,
10
11
  listSnapshots,
11
12
  getHeadCommit,
@@ -56,6 +57,21 @@ describe("lifecycle/git", () => {
56
57
  });
57
58
  });
58
59
 
60
+ test("readSnapshotAt reads a snapshot at a historical orphan-branch commit (#822)", async () => {
61
+ await withTestDir(async (dir) => {
62
+ await initRepo(dir);
63
+ await writeSnapshot("prod", "aws", JSON.stringify({ resources: { vpc: { type: "T", status: "OK" } } }), { cwd: dir });
64
+ await writeSnapshot("prod", "aws", JSON.stringify({ resources: { vpc: { type: "T", status: "OK" }, subnet: { type: "S", status: "OK" } } }), { cwd: dir });
65
+ const snaps = await listSnapshots({ cwd: dir }); // newest-first
66
+ expect(snaps.length).toBeGreaterThanOrEqual(2);
67
+ const older = await readSnapshotAt("prod", "aws", snaps[1].commit, { cwd: dir });
68
+ const newer = await readSnapshotAt("prod", "aws", snaps[0].commit, { cwd: dir });
69
+ expect(Object.keys(JSON.parse(older!).resources)).toEqual(["vpc"]);
70
+ expect(Object.keys(JSON.parse(newer!).resources).sort()).toEqual(["subnet", "vpc"]);
71
+ expect(await readSnapshotAt("prod", "gcp", snaps[0].commit, { cwd: dir })).toBeNull();
72
+ });
73
+ });
74
+
59
75
  test("subsequent writes preserve other env+lexicon entries", async () => {
60
76
  await withTestDir(async (dir) => {
61
77
  await initRepo(dir);
@@ -166,6 +166,27 @@ export async function readSnapshot(
166
166
  return readBlobFromPath(environment, `${lexicon}.json`, opts);
167
167
  }
168
168
 
169
+ /**
170
+ * Read a snapshot at a specific orphan-branch commit (#822). `readSnapshot` reads
171
+ * the branch tip; this reads `<ref>:<env>/<lexicon>.json` for any commit `ref`
172
+ * (as listed by {@link listSnapshots}), so two historical snapshots can be diffed.
173
+ * Returns null if that env/lexicon wasn't captured at `ref`.
174
+ */
175
+ export async function readSnapshotAt(
176
+ environment: string,
177
+ lexicon: string,
178
+ ref: string,
179
+ opts?: { cwd?: string },
180
+ ): Promise<string | null> {
181
+ const rt = getRuntime();
182
+ const result = await rt.spawn(
183
+ ["git", "show", `${ref}:${environment}/${lexicon}.json`],
184
+ { cwd: opts?.cwd },
185
+ );
186
+ if (result.exitCode !== 0) return null;
187
+ return result.stdout;
188
+ }
189
+
169
190
  /**
170
191
  * Append one immutable release record line to `<environment>/releases.jsonl`
171
192
  * on the orphan branch (#568, epic #551 "Build & deploy observability"). Same
@@ -1,5 +1,5 @@
1
1
  import { describe, test, expect } from "vitest";
2
- import { diffLive, diffLiveArtifacts } from "./live-diff";
2
+ import { diffLive, diffLiveArtifacts, diffSnapshots } from "./live-diff";
3
3
  import type { ResourceMetadata, ArtifactMetadata } from "../lexicon";
4
4
 
5
5
  const meta = (overrides: Partial<ResourceMetadata> = {}): ResourceMetadata => ({
@@ -182,3 +182,30 @@ describe("diffLiveArtifacts", () => {
182
182
  expect(result.unchanged).toEqual(["release/default/b"]);
183
183
  });
184
184
  });
185
+
186
+ describe("diffSnapshots (#822)", () => {
187
+ test("classifies added / removed / changed / unchanged between two snapshots", () => {
188
+ const prev = {
189
+ vpc: meta({ physicalId: "vpc-1" }),
190
+ subnet: meta({ physicalId: "subnet-1", status: "CREATE_COMPLETE" }),
191
+ gone: meta({ physicalId: "old" }),
192
+ };
193
+ const next = {
194
+ vpc: meta({ physicalId: "vpc-1" }), // unchanged
195
+ subnet: meta({ physicalId: "subnet-1", status: "UPDATE_COMPLETE" }), // changed (status)
196
+ fresh: meta({ physicalId: "new" }), // added
197
+ };
198
+ const d = diffSnapshots(prev, next);
199
+ expect(d.added).toEqual(["fresh"]);
200
+ expect(d.removed).toEqual(["gone"]);
201
+ expect(d.unchanged).toEqual(["vpc"]);
202
+ expect(d.changed.map((c) => c.name)).toEqual(["subnet"]);
203
+ expect(d.changed[0].changes.map((c) => c.path)).toContain("status");
204
+ });
205
+
206
+ test("empty vs empty is all-unchanged-empty; results are sorted", () => {
207
+ expect(diffSnapshots({}, {})).toEqual({ added: [], removed: [], changed: [], unchanged: [] });
208
+ const d = diffSnapshots({}, { b: meta(), a: meta() });
209
+ expect(d.added).toEqual(["a", "b"]);
210
+ });
211
+ });
@@ -90,6 +90,46 @@ function shallowEqual(a: unknown, b: unknown): boolean {
90
90
  return JSON.stringify(a) === JSON.stringify(b);
91
91
  }
92
92
 
93
+ /** The delta between two saved snapshots (#822): a two-way observed diff. */
94
+ export interface SnapshotDiffResult {
95
+ /** In `next`, not in `prev`. */
96
+ added: string[];
97
+ /** In `prev`, not in `next`. */
98
+ removed: string[];
99
+ /** In both; metadata differs (with the attribute-level changes). */
100
+ changed: ResourceDrift[];
101
+ /** In both; metadata identical. */
102
+ unchanged: string[];
103
+ }
104
+
105
+ /**
106
+ * Diff two **observed** snapshots (#822) — `prev` vs `next`, each a resources map
107
+ * read from the orphan branch. A two-way diff (no "declared" axis), so it answers
108
+ * "what changed in the cloud between two points" independent of source. Pure and
109
+ * deterministic (results sorted). Feeds the deployment-lanes frame-pair diff.
110
+ */
111
+ export function diffSnapshots(
112
+ prev: Record<string, ResourceMetadata>,
113
+ next: Record<string, ResourceMetadata>,
114
+ ): SnapshotDiffResult {
115
+ const added: string[] = [];
116
+ const removed: string[] = [];
117
+ const changed: ResourceDrift[] = [];
118
+ const unchanged: string[] = [];
119
+ for (const name of [...new Set([...Object.keys(prev), ...Object.keys(next)])].sort()) {
120
+ const a = prev[name];
121
+ const b = next[name];
122
+ if (!a && b) added.push(name);
123
+ else if (a && !b) removed.push(name);
124
+ else if (a && b) {
125
+ const changes = compareMetadata(a, b);
126
+ if (changes.length > 0) changed.push({ name, type: b.type ?? a.type, changes });
127
+ else unchanged.push(name);
128
+ }
129
+ }
130
+ return { added, removed, changed, unchanged };
131
+ }
132
+
93
133
  export function diffLive(input: DiffLiveInput): LiveDiffResult {
94
134
  const { declared, observedNow, observedThen } = input;
95
135
  const observedThenMap = observedThen ?? {};