@treeseed/sdk 0.10.28 → 0.11.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.
Files changed (148) hide show
  1. package/README.md +207 -6
  2. package/dist/capacity-provider.d.ts +3 -1
  3. package/dist/capacity-provider.js +25 -5
  4. package/dist/control-plane.d.ts +1 -0
  5. package/dist/control-plane.js +38 -13
  6. package/dist/db/market-schema.d.ts +8860 -6172
  7. package/dist/db/market-schema.js +108 -0
  8. package/dist/db/node-sqlite.js +7 -2
  9. package/dist/hosting/apps.d.ts +12 -0
  10. package/dist/hosting/apps.js +107 -0
  11. package/dist/hosting/builtins.d.ts +25 -0
  12. package/dist/hosting/builtins.js +791 -0
  13. package/dist/hosting/contracts.d.ts +207 -0
  14. package/dist/hosting/contracts.js +0 -0
  15. package/dist/hosting/graph.d.ts +192 -0
  16. package/dist/hosting/graph.js +1106 -0
  17. package/dist/hosting/index.d.ts +4 -0
  18. package/dist/hosting/index.js +4 -0
  19. package/dist/index.d.ts +10 -3
  20. package/dist/index.js +63 -6
  21. package/dist/managed-dependencies.js +1 -2
  22. package/dist/market-client.d.ts +63 -3
  23. package/dist/market-client.js +83 -11
  24. package/dist/operations/services/bootstrap-runner.d.ts +3 -1
  25. package/dist/operations/services/bootstrap-runner.js +22 -2
  26. package/dist/operations/services/config-runtime.d.ts +10 -5
  27. package/dist/operations/services/config-runtime.js +209 -66
  28. package/dist/operations/services/deploy.d.ts +70 -7
  29. package/dist/operations/services/deploy.js +579 -64
  30. package/dist/operations/services/deployment-readiness.d.ts +30 -0
  31. package/dist/operations/services/deployment-readiness.js +175 -0
  32. package/dist/operations/services/git-workflow.d.ts +2 -1
  33. package/dist/operations/services/git-workflow.js +9 -3
  34. package/dist/operations/services/github-actions-verification.d.ts +1 -0
  35. package/dist/operations/services/github-actions-verification.js +1 -0
  36. package/dist/operations/services/github-api.js +1 -1
  37. package/dist/operations/services/github-automation.d.ts +1 -1
  38. package/dist/operations/services/github-automation.js +4 -3
  39. package/dist/operations/services/github-credentials.d.ts +13 -0
  40. package/dist/operations/services/github-credentials.js +58 -0
  41. package/dist/operations/services/hosted-service-checks.d.ts +63 -0
  42. package/dist/operations/services/hosted-service-checks.js +327 -0
  43. package/dist/operations/services/hub-provider-launch.js +3 -3
  44. package/dist/operations/services/live-hosted-service-checks.d.ts +25 -0
  45. package/dist/operations/services/live-hosted-service-checks.js +350 -0
  46. package/dist/operations/services/managed-host-security.js +1 -1
  47. package/dist/operations/services/operations-runner-smoke.d.ts +30 -0
  48. package/dist/operations/services/operations-runner-smoke.js +180 -0
  49. package/dist/operations/services/package-adapters.d.ts +95 -0
  50. package/dist/operations/services/package-adapters.js +288 -0
  51. package/dist/operations/services/package-reference-policy.d.ts +1 -0
  52. package/dist/operations/services/package-reference-policy.js +15 -2
  53. package/dist/operations/services/project-platform.d.ts +80 -22
  54. package/dist/operations/services/project-platform.js +49 -8
  55. package/dist/operations/services/project-web-monitor.js +26 -4
  56. package/dist/operations/services/railway-api.d.ts +88 -5
  57. package/dist/operations/services/railway-api.js +626 -35
  58. package/dist/operations/services/railway-deploy.d.ts +46 -40
  59. package/dist/operations/services/railway-deploy.js +261 -293
  60. package/dist/operations/services/release-candidate.d.ts +19 -0
  61. package/dist/operations/services/release-candidate.js +375 -38
  62. package/dist/operations/services/repository-save-orchestrator.d.ts +3 -1
  63. package/dist/operations/services/repository-save-orchestrator.js +279 -66
  64. package/dist/operations/services/runtime-tools.d.ts +1 -0
  65. package/dist/operations/services/runtime-tools.js +10 -9
  66. package/dist/operations/services/verification-cache.d.ts +25 -0
  67. package/dist/operations/services/verification-cache.js +71 -0
  68. package/dist/operations/services/workspace-dependency-mode.js +9 -1
  69. package/dist/operations/services/workspace-save.js +1 -1
  70. package/dist/operations/services/workspace-tools.js +2 -1
  71. package/dist/platform/contracts.d.ts +32 -1
  72. package/dist/platform/deploy-config.js +73 -8
  73. package/dist/platform/env.yaml +163 -35
  74. package/dist/platform/environment.d.ts +1 -0
  75. package/dist/platform/environment.js +74 -5
  76. package/dist/platform/plugin.d.ts +9 -0
  77. package/dist/platform-operation-store.js +2 -2
  78. package/dist/platform-operations.js +1 -1
  79. package/dist/reconcile/bootstrap-systems.js +2 -2
  80. package/dist/reconcile/builtin-adapters.js +372 -189
  81. package/dist/reconcile/contracts.d.ts +9 -5
  82. package/dist/reconcile/desired-state.d.ts +1 -0
  83. package/dist/reconcile/desired-state.js +5 -5
  84. package/dist/reconcile/engine.d.ts +5 -2
  85. package/dist/reconcile/engine.js +53 -32
  86. package/dist/reconcile/index.d.ts +2 -0
  87. package/dist/reconcile/index.js +2 -0
  88. package/dist/reconcile/live-acceptance.d.ts +79 -0
  89. package/dist/reconcile/live-acceptance.js +1615 -0
  90. package/dist/reconcile/platform.d.ts +104 -0
  91. package/dist/reconcile/platform.js +100 -0
  92. package/dist/reconcile/state.js +4 -4
  93. package/dist/reconcile/units.js +2 -2
  94. package/dist/scripts/deployment-readiness.js +20 -0
  95. package/dist/scripts/generate-treedx-openapi-types.js +186 -0
  96. package/dist/scripts/operations-runner-smoke.js +16 -0
  97. package/dist/scripts/release-verify.js +4 -1
  98. package/dist/scripts/tenant-workflow-action.js +10 -1
  99. package/dist/sdk-types.d.ts +169 -4
  100. package/dist/sdk-types.js +20 -2
  101. package/dist/sdk.d.ts +35 -24
  102. package/dist/sdk.js +186 -17
  103. package/dist/template-launch-requirements.js +9 -0
  104. package/dist/treedx/adapters.d.ts +6 -0
  105. package/dist/treedx/adapters.js +36 -0
  106. package/dist/treedx/client.d.ts +222 -0
  107. package/dist/treedx/client.js +871 -0
  108. package/dist/treedx/errors.d.ts +13 -0
  109. package/dist/treedx/errors.js +17 -0
  110. package/dist/treedx/federated-client.d.ts +27 -0
  111. package/dist/treedx/federated-client.js +158 -0
  112. package/dist/treedx/generated/openapi-types.d.ts +3558 -0
  113. package/dist/treedx/generated/openapi-types.js +0 -0
  114. package/dist/treedx/graph-adapter.d.ts +33 -0
  115. package/dist/treedx/graph-adapter.js +156 -0
  116. package/dist/treedx/index.d.ts +14 -0
  117. package/dist/treedx/index.js +48 -0
  118. package/dist/treedx/market-integration.d.ts +27 -0
  119. package/dist/treedx/market-integration.js +131 -0
  120. package/dist/treedx/ports.d.ts +166 -0
  121. package/dist/treedx/ports.js +231 -0
  122. package/dist/treedx/query-adapter.d.ts +19 -0
  123. package/dist/treedx/query-adapter.js +62 -0
  124. package/dist/treedx/registry-client.d.ts +11 -0
  125. package/dist/treedx/registry-client.js +19 -0
  126. package/dist/treedx/repository-adapter.d.ts +45 -0
  127. package/dist/treedx/repository-adapter.js +308 -0
  128. package/dist/treedx/sdk-integration.d.ts +27 -0
  129. package/dist/treedx/sdk-integration.js +63 -0
  130. package/dist/treedx/types.d.ts +1084 -0
  131. package/dist/treedx/types.js +8 -0
  132. package/dist/treedx/workspace-adapter.d.ts +27 -0
  133. package/dist/treedx/workspace-adapter.js +65 -0
  134. package/dist/treedx-backends.d.ts +218 -0
  135. package/dist/treedx-backends.js +632 -0
  136. package/dist/treedx-client.d.ts +86 -0
  137. package/dist/treedx-client.js +175 -0
  138. package/dist/treeseed/template-catalog/catalog.fixture.json +23 -23
  139. package/dist/workflow/operations.d.ts +119 -13
  140. package/dist/workflow/operations.js +309 -53
  141. package/dist/workflow-state.d.ts +13 -0
  142. package/dist/workflow-state.js +43 -26
  143. package/dist/workflow-support.d.ts +11 -3
  144. package/dist/workflow-support.js +67 -3
  145. package/dist/workflow.d.ts +5 -0
  146. package/drizzle/market/0004_treedx_market_integration.sql +99 -0
  147. package/package.json +34 -3
  148. package/templates/github/deploy-web.workflow.yml +39 -6
@@ -1,9 +1,9 @@
1
1
  import type { TreeseedDeployConfig } from '../platform/contracts.ts';
2
2
  export type TreeseedReconcileProviderId = string;
3
- export type TreeseedReconcileActionKind = 'noop' | 'create' | 'update' | 'reuse' | 'drift_correct' | 'destroy';
3
+ export type TreeseedReconcileActionKind = 'noop' | 'create' | 'update' | 'replace' | 'delete' | 'adopt' | 'rename' | 'reattach' | 'retain' | 'taint' | 'blocked';
4
4
  export type TreeseedReconcileStatusKind = 'pending' | 'ready' | 'drifted' | 'error';
5
5
  export type TreeseedReconcileVerificationSource = 'cli' | 'api' | 'sdk' | 'derived';
6
- export type TreeseedReconcileUnitType = 'web-ui' | 'api-runtime' | 'market-operations-runner-runtime' | 'workday-manager-runtime' | 'worker-runner-runtime' | 'edge-worker' | 'content-store' | 'queue' | 'database' | 'kv-form-guard' | 'turnstile-widget' | 'pages-project' | 'custom-domain:web' | 'custom-domain:api' | 'dns-record' | 'railway-service:api' | 'railway-service:market-operations-runner' | 'railway-service:workday-manager' | 'railway-service:worker-runner';
6
+ export type TreeseedReconcileUnitType = 'web-ui' | 'api-runtime' | 'operations-runner-runtime' | 'workday-manager-runtime' | 'worker-runner-runtime' | 'edge-worker' | 'content-store' | 'queue' | 'database' | 'kv-form-guard' | 'turnstile-widget' | 'pages-project' | 'custom-domain:web' | 'custom-domain:api' | 'dns-record' | 'railway-service:api' | 'railway-service:operations-runner' | 'railway-service:workday-manager' | 'railway-service:worker-runner';
7
7
  export type TreeseedReconcileTarget = {
8
8
  kind: 'persistent';
9
9
  scope: 'local' | 'staging' | 'prod';
@@ -115,6 +115,7 @@ export interface TreeseedReconcileRunContext {
115
115
  target: TreeseedReconcileTarget;
116
116
  deployConfig: TreeseedDeployConfig;
117
117
  launchEnv: NodeJS.ProcessEnv;
118
+ dryRun?: boolean;
118
119
  write?: (line: string) => void;
119
120
  session: Map<string, unknown>;
120
121
  }
@@ -129,11 +130,11 @@ export interface TreeseedReconcileAdapter {
129
130
  supports(unitType: TreeseedReconcileUnitType, providerId: TreeseedReconcileProviderId): boolean;
130
131
  validate?(input: TreeseedReconcileAdapterInput): Promise<void> | void;
131
132
  requiredPostconditions?(input: TreeseedReconcileAdapterInput): Promise<TreeseedUnitPostcondition[]> | TreeseedUnitPostcondition[];
132
- observe(input: TreeseedReconcileAdapterInput): Promise<TreeseedObservedUnitState> | TreeseedObservedUnitState;
133
- plan(input: TreeseedReconcileAdapterInput & {
133
+ refresh(input: TreeseedReconcileAdapterInput): Promise<TreeseedObservedUnitState> | TreeseedObservedUnitState;
134
+ diff(input: TreeseedReconcileAdapterInput & {
134
135
  observed: TreeseedObservedUnitState;
135
136
  }): Promise<TreeseedUnitDiff> | TreeseedUnitDiff;
136
- reconcile(input: TreeseedReconcileAdapterInput & {
137
+ apply(input: TreeseedReconcileAdapterInput & {
137
138
  observed: TreeseedObservedUnitState;
138
139
  diff: TreeseedUnitDiff;
139
140
  }): Promise<TreeseedReconcileResult> | TreeseedReconcileResult;
@@ -146,6 +147,9 @@ export interface TreeseedReconcileAdapter {
146
147
  destroy?(input: TreeseedReconcileAdapterInput & {
147
148
  observed: TreeseedObservedUnitState;
148
149
  }): Promise<TreeseedReconcileResult> | TreeseedReconcileResult;
150
+ importOrAdopt?(input: TreeseedReconcileAdapterInput & {
151
+ observed: TreeseedObservedUnitState;
152
+ }): Promise<TreeseedReconcileResult> | TreeseedReconcileResult;
149
153
  }
150
154
  export interface TreeseedReconcileStateRecord {
151
155
  version: 1;
@@ -38,6 +38,7 @@ export declare function deriveTreeseedDesiredUnits({ tenantRoot, target, }: {
38
38
  previewProjectName: string | undefined;
39
39
  productionBranch: string;
40
40
  stagingBranch: string;
41
+ buildCommand: string | undefined;
41
42
  buildOutputDir: string | undefined;
42
43
  } | undefined;
43
44
  r2: {
@@ -11,8 +11,8 @@ function railwayConcreteUnitTypeForServiceKey(serviceKey) {
11
11
  switch (serviceKey) {
12
12
  case "api":
13
13
  return "railway-service:api";
14
- case "marketOperationsRunner":
15
- return "railway-service:market-operations-runner";
14
+ case "operationsRunner":
15
+ return "railway-service:operations-runner";
16
16
  case "workdayManager":
17
17
  return "railway-service:workday-manager";
18
18
  case "workerRunner":
@@ -204,7 +204,7 @@ function deriveTreeseedDesiredUnits({
204
204
  const scope = target.kind === "persistent" ? target.scope : "staging";
205
205
  for (const configuredService of configuredRailwayServices(tenantRoot, scope)) {
206
206
  const serviceKey = configuredService.key;
207
- const service = deployConfig.services?.[serviceKey];
207
+ const service = configuredService.serviceConfig ?? deployConfig.services?.[serviceKey];
208
208
  const serviceState = legacyState.services?.[serviceKey];
209
209
  if (!service || service.enabled === false || service.provider !== "railway") {
210
210
  continue;
@@ -284,8 +284,8 @@ function deriveTreeseedDesiredUnits({
284
284
  switch (serviceKey) {
285
285
  case "api":
286
286
  return "api-runtime";
287
- case "marketOperationsRunner":
288
- return "market-operations-runner-runtime";
287
+ case "operationsRunner":
288
+ return "operations-runner-runtime";
289
289
  case "workdayManager":
290
290
  return "workday-manager-runtime";
291
291
  case "workerRunner":
@@ -1,7 +1,7 @@
1
1
  import type { TreeseedObservedUnitState, TreeseedReconcilePlan, TreeseedReconcileResult, TreeseedReconcileStateRecord, TreeseedReconcileTarget, TreeseedUnitVerificationResult } from './contracts.ts';
2
2
  import { type TreeseedRunnableBootstrapSystem } from './bootstrap-systems.ts';
3
3
  import { type TreeseedTimingEntry } from '../timing.ts';
4
- export declare function observeTreeseedUnits({ tenantRoot, target, env, systems, write, }: {
4
+ export declare function refreshTreeseedUnits({ tenantRoot, target, env, systems, write, }: {
5
5
  tenantRoot: string;
6
6
  target: TreeseedReconcileTarget;
7
7
  env?: NodeJS.ProcessEnv;
@@ -46,6 +46,7 @@ export declare function observeTreeseedUnits({ tenantRoot, target, env, systems,
46
46
  previewProjectName: string | undefined;
47
47
  productionBranch: string;
48
48
  stagingBranch: string;
49
+ buildCommand: string | undefined;
49
50
  buildOutputDir: string | undefined;
50
51
  } | undefined;
51
52
  r2: {
@@ -249,6 +250,7 @@ export declare function planTreeseedReconciliation({ tenantRoot, target, env, sy
249
250
  previewProjectName: string | undefined;
250
251
  productionBranch: string;
251
252
  stagingBranch: string;
253
+ buildCommand: string | undefined;
252
254
  buildOutputDir: string | undefined;
253
255
  } | undefined;
254
256
  r2: {
@@ -406,12 +408,13 @@ export declare function planTreeseedReconciliation({ tenantRoot, target, env, sy
406
408
  };
407
409
  };
408
410
  }>;
409
- export declare function reconcileTreeseedTarget({ tenantRoot, target, env, systems, write, }: {
411
+ export declare function reconcileTreeseedTarget({ tenantRoot, target, env, systems, write, dryRun, }: {
410
412
  tenantRoot: string;
411
413
  target: TreeseedReconcileTarget;
412
414
  env?: NodeJS.ProcessEnv;
413
415
  systems?: TreeseedRunnableBootstrapSystem[];
414
416
  write?: (line: string) => void;
417
+ dryRun?: boolean;
415
418
  }): Promise<{
416
419
  target: TreeseedReconcileTarget;
417
420
  units: import("./contracts.ts").TreeseedDesiredUnit[];
@@ -26,13 +26,14 @@ function formatVerificationFailure(verification) {
26
26
  ].filter(Boolean);
27
27
  return details.length > 0 ? `Verification failed (${details.join(" | ")})` : "Verification failed.";
28
28
  }
29
- function createRunContext(tenantRoot, target, launchEnv, write) {
29
+ function createRunContext(tenantRoot, target, launchEnv, write, dryRun = false) {
30
30
  const { deployConfig } = deriveTreeseedDesiredUnits({ tenantRoot, target });
31
31
  return {
32
32
  tenantRoot,
33
33
  target,
34
34
  deployConfig,
35
35
  launchEnv,
36
+ dryRun,
36
37
  write,
37
38
  session: /* @__PURE__ */ new Map()
38
39
  };
@@ -123,7 +124,7 @@ async function verifyPlanUnits({
123
124
  }
124
125
  return verificationResults;
125
126
  }
126
- async function observeTreeseedUnits({
127
+ async function refreshTreeseedUnits({
127
128
  tenantRoot,
128
129
  target,
129
130
  env = process.env,
@@ -138,18 +139,18 @@ async function observeTreeseedUnits({
138
139
  const context = createRunContext(tenantRoot, target, env, write);
139
140
  const observations = /* @__PURE__ */ new Map();
140
141
  await runByDependencyLevel(topologicallySortDesiredUnits(units), async (unit) => {
141
- write?.(`Observing ${unit.provider}:${unit.unitType}...`);
142
+ write?.(`Refreshing ${unit.provider}:${unit.unitType}...`);
142
143
  const adapter = registry.get(unit.unitType, unit.provider);
143
144
  const persisted = ensureTreeseedPersistedUnitState(reconcileState, unit);
144
145
  let observed;
145
146
  try {
146
- observed = await Promise.resolve(adapter.observe({
147
+ observed = await Promise.resolve(adapter.refresh({
147
148
  context,
148
149
  unit,
149
150
  persistedState: persisted
150
151
  }));
151
152
  } catch (error) {
152
- wrapAdapterFailure("observe", unit.provider, unit.unitType, unit.unitId, error);
153
+ wrapAdapterFailure("refresh", unit.provider, unit.unitType, unit.unitId, error);
153
154
  }
154
155
  observations.set(unit.unitId, observed);
155
156
  });
@@ -167,7 +168,7 @@ async function planTreeseedReconciliation({
167
168
  systems,
168
169
  write
169
170
  }) {
170
- const observed = await observeTreeseedUnits({ tenantRoot, target, env, systems, write });
171
+ const observed = await refreshTreeseedUnits({ tenantRoot, target, env, systems, write });
171
172
  const registry = createTreeseedReconcileRegistry(observed.deployConfig);
172
173
  const context = createRunContext(tenantRoot, target, env, write);
173
174
  const plans = [];
@@ -177,14 +178,14 @@ async function planTreeseedReconciliation({
177
178
  const observation = observed.observations.get(unit.unitId);
178
179
  let diff;
179
180
  try {
180
- diff = await Promise.resolve(adapter.plan({
181
+ diff = await Promise.resolve(adapter.diff({
181
182
  context,
182
183
  unit,
183
184
  persistedState: persisted,
184
185
  observed: observation
185
186
  }));
186
187
  } catch (error) {
187
- wrapAdapterFailure("plan", unit.provider, unit.unitType, unit.unitId, error);
188
+ wrapAdapterFailure("diff", unit.provider, unit.unitType, unit.unitId, error);
188
189
  }
189
190
  plans.push({
190
191
  unit,
@@ -203,11 +204,12 @@ async function reconcileTreeseedTarget({
203
204
  target,
204
205
  env = process.env,
205
206
  systems,
206
- write
207
+ write,
208
+ dryRun = false
207
209
  }) {
208
210
  const planned = await planTreeseedReconciliation({ tenantRoot, target, env, systems, write });
209
211
  const registry = createTreeseedReconcileRegistry(planned.deployConfig);
210
- const context = createRunContext(tenantRoot, target, env, write);
212
+ const context = createRunContext(tenantRoot, target, env, write, dryRun);
211
213
  const results = [];
212
214
  const verificationMap = /* @__PURE__ */ new Map();
213
215
  const timingEntries = [];
@@ -225,7 +227,7 @@ async function reconcileTreeseedTarget({
225
227
  await runByDependencyLevel(topologicallySortDesiredUnits(planned.units), async (unit) => {
226
228
  const plan = planByUnitId.get(unit.unitId);
227
229
  const unitTiming = {
228
- name: `reconcile:${plan.unit.provider}:${plan.unit.unitType}:${plan.unit.logicalName}`,
230
+ name: `apply:${plan.unit.provider}:${plan.unit.unitType}:${plan.unit.logicalName}`,
229
231
  durationMs: 0,
230
232
  status: "running",
231
233
  children: [],
@@ -233,7 +235,7 @@ async function reconcileTreeseedTarget({
233
235
  };
234
236
  const unitStartMs = performance.now();
235
237
  timingEntries.push(unitTiming);
236
- write?.(`Reconciling ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName})...`);
238
+ write?.(`Applying ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName})...`);
237
239
  const adapter = registry.get(plan.unit.unitType, plan.unit.provider);
238
240
  const persisted = ensureTreeseedPersistedUnitState(planned.state, plan.unit);
239
241
  try {
@@ -261,27 +263,40 @@ async function reconcileTreeseedTarget({
261
263
  let result;
262
264
  try {
263
265
  const stageStartMs = performance.now();
264
- result = await Promise.resolve(adapter.reconcile({
265
- context,
266
- unit: plan.unit,
267
- persistedState: persisted,
268
- observed: plan.observed,
269
- diff: plan.diff
270
- }));
266
+ if (dryRun) {
267
+ result = {
268
+ unit: plan.unit,
269
+ observed: plan.observed,
270
+ diff: plan.diff,
271
+ action: plan.diff.action,
272
+ warnings: [...plan.observed.warnings, "dry run: apply skipped"],
273
+ resourceLocators: plan.observed.locators,
274
+ state: plan.observed.live,
275
+ verification: null
276
+ };
277
+ } else {
278
+ result = await Promise.resolve(adapter.apply({
279
+ context,
280
+ unit: plan.unit,
281
+ persistedState: persisted,
282
+ observed: plan.observed,
283
+ diff: plan.diff
284
+ }));
285
+ }
271
286
  unitTiming.children?.push({
272
- name: `${unitTiming.name}:reconcile`,
287
+ name: `${unitTiming.name}:apply`,
273
288
  durationMs: elapsedMs(stageStartMs),
274
- status: "success"
289
+ status: dryRun ? "skipped" : "success"
275
290
  });
276
291
  } catch (error) {
277
292
  unitTiming.children?.push({
278
- name: `${unitTiming.name}:reconcile`,
293
+ name: `${unitTiming.name}:apply`,
279
294
  durationMs: elapsedMs(unitStartMs),
280
295
  status: "failed"
281
296
  });
282
297
  unitTiming.durationMs = elapsedMs(unitStartMs);
283
298
  unitTiming.status = "failed";
284
- wrapAdapterFailure("reconcile", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
299
+ wrapAdapterFailure("apply", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
285
300
  }
286
301
  write?.(`Verifying ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName})...`);
287
302
  const postconditions = await Promise.resolve(adapter.requiredPostconditions?.({
@@ -329,13 +344,19 @@ async function reconcileTreeseedTarget({
329
344
  unitTiming.status = verification.verified ? "success" : "failed";
330
345
  write?.(`Finished ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName}) in ${formatDurationMs(unitTiming.durationMs)}.`);
331
346
  if (!verification.verified) {
332
- await persistVerifiedResult(persisted, verifiedResult);
347
+ if (!dryRun) {
348
+ await persistVerifiedResult(persisted, verifiedResult);
349
+ }
333
350
  throw new Error(`Treeseed reconcile verification failed for ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.unitId}): ${formatVerificationFailure(verification)}`);
334
351
  }
335
- await persistVerifiedResult(persisted, verifiedResult);
352
+ if (!dryRun) {
353
+ await persistVerifiedResult(persisted, verifiedResult);
354
+ }
336
355
  results.push(verifiedResult);
337
356
  });
338
- writeTreeseedReconcileState(tenantRoot, planned.state);
357
+ if (!dryRun) {
358
+ writeTreeseedReconcileState(tenantRoot, planned.state);
359
+ }
339
360
  return {
340
361
  target,
341
362
  units: planned.units,
@@ -365,13 +386,13 @@ async function destroyTreeseedTargetUnits({
365
386
  const persisted = ensureTreeseedPersistedUnitState(reconcileState, unit);
366
387
  let observed;
367
388
  try {
368
- observed = await Promise.resolve(adapter.observe({
389
+ observed = await Promise.resolve(adapter.refresh({
369
390
  context,
370
391
  unit,
371
392
  persistedState: persisted
372
393
  }));
373
394
  } catch (error) {
374
- wrapAdapterFailure("observe", unit.provider, unit.unitType, unit.unitId, error);
395
+ wrapAdapterFailure("refresh", unit.provider, unit.unitType, unit.unitId, error);
375
396
  }
376
397
  let result;
377
398
  try {
@@ -394,14 +415,14 @@ async function collectTreeseedReconcileStatus({
394
415
  env = process.env,
395
416
  systems
396
417
  }) {
397
- const observed = await observeTreeseedUnits({ tenantRoot, target, env, systems });
418
+ const observed = await refreshTreeseedUnits({ tenantRoot, target, env, systems });
398
419
  const registry = createTreeseedReconcileRegistry(observed.deployConfig);
399
420
  const context = createRunContext(tenantRoot, target, env);
400
421
  const plans = await Promise.all(observed.units.map(async (unit) => {
401
422
  const adapter = registry.get(unit.unitType, unit.provider);
402
423
  const persisted = ensureTreeseedPersistedUnitState(observed.state, unit);
403
424
  const observation = observed.observations.get(unit.unitId);
404
- const diff = await Promise.resolve(adapter.plan({
425
+ const diff = await Promise.resolve(adapter.diff({
405
426
  context,
406
427
  unit,
407
428
  persistedState: persisted,
@@ -448,7 +469,7 @@ async function collectTreeseedReconcileStatus({
448
469
  export {
449
470
  collectTreeseedReconcileStatus,
450
471
  destroyTreeseedTargetUnits,
451
- observeTreeseedUnits,
452
472
  planTreeseedReconciliation,
453
- reconcileTreeseedTarget
473
+ reconcileTreeseedTarget,
474
+ refreshTreeseedUnits
454
475
  };
@@ -1,8 +1,10 @@
1
1
  export * from './contracts.ts';
2
+ export * from './platform.ts';
2
3
  export * from './bootstrap-systems.ts';
3
4
  export * from './desired-state.ts';
4
5
  export * from './engine.ts';
5
6
  export * from './errors.ts';
7
+ export * from './live-acceptance.ts';
6
8
  export * from './registry.ts';
7
9
  export * from './state.ts';
8
10
  export * from './units.ts';
@@ -1,8 +1,10 @@
1
1
  export * from "./contracts.js";
2
+ export * from "./platform.js";
2
3
  export * from "./bootstrap-systems.js";
3
4
  export * from "./desired-state.js";
4
5
  export * from "./engine.js";
5
6
  export * from "./errors.js";
7
+ export * from "./live-acceptance.js";
6
8
  export * from "./registry.js";
7
9
  export * from "./state.js";
8
10
  export * from "./units.js";
@@ -0,0 +1,79 @@
1
+ import { type TreeseedCanonicalAction, type TreeseedCanonicalDrift, type TreeseedCanonicalGraphNode, type TreeseedCanonicalReconcileReport } from './platform.ts';
2
+ export type TreeseedLiveReconcileProvider = 'railway' | 'cloudflare' | 'github' | 'local';
3
+ export type TreeseedLiveReconcileMode = 'smoke' | 'acceptance' | 'cleanup';
4
+ export type TreeseedLiveReconcileEnvironment = 'local' | 'staging' | 'prod';
5
+ export interface TreeseedLiveReconcileScenarioResult {
6
+ id: string;
7
+ provider: TreeseedLiveReconcileProvider;
8
+ capability: string;
9
+ mode: TreeseedLiveReconcileMode;
10
+ ok: boolean;
11
+ phase: 'smoke' | 'validate' | 'create' | 'update' | 'replace' | 'verify' | 'destroy' | 'cleanup' | 'blocked';
12
+ action: TreeseedCanonicalAction['kind'];
13
+ reason: string;
14
+ startedAt: string;
15
+ completedAt: string;
16
+ durationMs: number;
17
+ locators: Record<string, string | null>;
18
+ createdResources: TreeseedCanonicalGraphNode[];
19
+ updatedResources: TreeseedCanonicalGraphNode[];
20
+ replacedResources: TreeseedCanonicalGraphNode[];
21
+ destroyedResources: TreeseedCanonicalGraphNode[];
22
+ retainedResources: TreeseedCanonicalGraphNode[];
23
+ issues: string[];
24
+ }
25
+ export interface TreeseedLiveReconcileProviderReport {
26
+ provider: TreeseedLiveReconcileProvider;
27
+ mode: TreeseedLiveReconcileMode;
28
+ runId: string;
29
+ resourcePrefix: string;
30
+ scenarioResults: TreeseedLiveReconcileScenarioResult[];
31
+ coverage: {
32
+ total: number;
33
+ passed: number;
34
+ failed: number;
35
+ capabilities: string[];
36
+ };
37
+ createdResources: TreeseedCanonicalGraphNode[];
38
+ updatedResources: TreeseedCanonicalGraphNode[];
39
+ replacedResources: TreeseedCanonicalGraphNode[];
40
+ destroyedResources: TreeseedCanonicalGraphNode[];
41
+ retainedResources: TreeseedCanonicalGraphNode[];
42
+ cleanupDrift: TreeseedCanonicalDrift[];
43
+ report: TreeseedCanonicalReconcileReport;
44
+ ok: boolean;
45
+ }
46
+ export interface TreeseedLiveReconcileRunResult {
47
+ command: 'reconcile test-live';
48
+ mode: TreeseedLiveReconcileMode;
49
+ environment: TreeseedLiveReconcileEnvironment;
50
+ runId: string;
51
+ resourcePrefix: string;
52
+ providers: TreeseedLiveReconcileProviderReport[];
53
+ ok: boolean;
54
+ }
55
+ export interface TreeseedLiveReconcileProgressEvent {
56
+ provider: TreeseedLiveReconcileProvider;
57
+ mode: TreeseedLiveReconcileMode;
58
+ environment: TreeseedLiveReconcileEnvironment;
59
+ runId: string;
60
+ resourcePrefix: string;
61
+ capability?: string;
62
+ phase: 'start' | 'cleanup' | 'create' | 'verify' | 'destroy' | 'complete' | 'blocked';
63
+ message: string;
64
+ elapsedMs?: number;
65
+ }
66
+ export interface RunTreeseedLiveReconcileTestsOptions {
67
+ cwd: string;
68
+ environment: TreeseedLiveReconcileEnvironment;
69
+ providers: TreeseedLiveReconcileProvider[];
70
+ mode?: TreeseedLiveReconcileMode;
71
+ env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
72
+ runId?: string;
73
+ now?: Date;
74
+ fetchImpl?: typeof fetch;
75
+ onProgress?: (event: TreeseedLiveReconcileProgressEvent) => void;
76
+ }
77
+ export declare function treeseedLiveReconcileProviderCapabilities(provider: TreeseedLiveReconcileProvider): string[];
78
+ export declare function treeseedLiveReconcileResourcePrefix(environment: TreeseedLiveReconcileEnvironment, provider: TreeseedLiveReconcileProvider, runId: string): string;
79
+ export declare function runTreeseedLiveReconcileTests(options: RunTreeseedLiveReconcileTestsOptions): Promise<TreeseedLiveReconcileRunResult>;