gencow 0.1.213 → 0.1.215

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.
@@ -16,7 +16,14 @@ const APP_DELETE_ACTIVE_STATES = new Set([
16
16
  "failed_tenant_db_cleanup",
17
17
  "failed_catalog_cleanup",
18
18
  ]);
19
- const APP_DELETE_TERMINAL_FAILURE_STATES = new Set(["blocked_catalog_outcome_unknown"]);
19
+ const APP_DELETE_TERMINAL_FAILURE_STATES = new Set([
20
+ "blocked_catalog_outcome_unknown",
21
+ "failed_delete_recovery_required",
22
+ ]);
23
+ const APP_DELETE_RESPONSE_STATES = new Set([
24
+ ...APP_DELETE_ACTIVE_STATES,
25
+ ...APP_DELETE_TERMINAL_FAILURE_STATES,
26
+ ]);
20
27
  const CORRELATION_ID_PATTERN = /^[A-Za-z0-9_-]{8,128}$/u;
21
28
  const DIAGNOSTIC_CODE_PATTERN = /^[A-Za-z][A-Za-z0-9_]{2,127}$/u;
22
29
  const APP_ID_PATTERN = /^(?=.{3,63}$)[a-z][a-z0-9]*(?:-[a-z0-9]+){2,7}$/u;
@@ -44,7 +51,7 @@ export function parseAppDeleteOperationEnvelope(value, expectedAppId) {
44
51
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
45
52
  if (value.success !== undefined && value.success !== false) return null;
46
53
  if (!APP_DELETE_OPERATION_PATTERN.test(value.operationId ?? "")) return null;
47
- if (!APP_DELETE_ACTIVE_STATES.has(value.state)) return null;
54
+ if (!APP_DELETE_RESPONSE_STATES.has(value.state)) return null;
48
55
  if (
49
56
  !CORRELATION_ID_PATTERN.test(value.correlationId ?? "") ||
50
57
  (value.deleted !== undefined && value.deleted !== expectedAppId) ||
@@ -87,7 +94,8 @@ function isMatchingTerminalFailureResponse(value, accepted, expectedAppId) {
87
94
  value.deleted === expectedAppId &&
88
95
  value.operationId === accepted.operationId &&
89
96
  value.correlationId === accepted.correlationId &&
90
- APP_DELETE_TERMINAL_FAILURE_STATES.has(value.state) &&
97
+ (APP_DELETE_TERMINAL_FAILURE_STATES.has(value.state) ||
98
+ (value.state === "blocked_active_work" && value.code === "PLATFORM_ACTION_REQUIRED")) &&
91
99
  DIAGNOSTIC_CODE_PATTERN.test(value.code ?? "")
92
100
  );
93
101
  }
@@ -103,6 +111,9 @@ export async function pollAppDeleteOperation({
103
111
  }) {
104
112
  const accepted = parseAppDeleteOperationEnvelope(initialBody, name);
105
113
  if (!accepted) return null;
114
+ if (isMatchingTerminalFailureResponse(initialBody, accepted, name)) {
115
+ return { ...initialBody, error: initialBody.code };
116
+ }
106
117
 
107
118
  let latest = accepted;
108
119
  let successfulPollResponses = 0;
@@ -179,6 +179,7 @@ export async function verifyAppReady(params) {
179
179
  successImpl = success,
180
180
  warnImpl = warn,
181
181
  infoImpl = info,
182
+ announceReady = true,
182
183
  } = params;
183
184
  let readinessUrl = appUrl;
184
185
  if (readinessBaseUrl) {
@@ -232,9 +233,9 @@ export async function verifyAppReady(params) {
232
233
  writeStdoutImpl("\r" + " ".repeat(50) + "\r");
233
234
 
234
235
  const healthElapsed = ((nowImpl() - start) / 1000).toFixed(1);
235
- if (healthy) {
236
+ if (healthy && announceReady) {
236
237
  successImpl(`App Ready! (${healthElapsed}s)`);
237
- } else {
238
+ } else if (!healthy) {
238
239
  warnImpl(`App readiness check failed (${healthElapsed}s).`);
239
240
  if (lastStatus !== null) infoImpl(`Last response: HTTP ${lastStatus}`);
240
241
  infoImpl(`Manual check: ${CYAN}${appUrl}${RESET}`);
@@ -748,7 +748,7 @@ export function createDeployPackageRuntime({
748
748
  successImpl(`Server build complete! (${deployElapsed}s)`);
749
749
 
750
750
  if (deployData.url) {
751
- const ready = await verifyAppReadyImpl({ appUrl: deployData.url, appId: targetAppId });
751
+ const ready = await verifyAppReadyImpl(releaseAttemptId ? { appUrl: deployData.url, appId: targetAppId, announceReady: false } : { appUrl: deployData.url, appId: targetAppId });
752
752
  if (!ready) {
753
753
  errorImpl("Deploy failed: app did not become ready after deployment.");
754
754
  await showCrashLogs(null, "Readiness failure diagnostics:", targetAppId);
@@ -659,6 +659,7 @@ export function createDeployCommand({
659
659
  platformFetchImpl,
660
660
  })
661
661
  : undefined,
662
+ successImpl,
662
663
  });
663
664
  return 0;
664
665
  }
@@ -2,7 +2,7 @@ import { existsSync, unlinkSync, writeFileSync } from "fs";
2
2
  import { resolve } from "path";
3
3
 
4
4
  import { createDeployPackageRuntime, writeProjectMetadata } from "./deploy-package-runtime.mjs";
5
- import { BOLD, CYAN, DIM, RESET, error, info, log } from "./output.mjs";
5
+ import { BOLD, CYAN, DIM, RESET, error, info, log, success } from "./output.mjs";
6
6
  import { preflightStaticDeployArtifact } from "./static-deploy-command.mjs";
7
7
 
8
8
  export function createScopedDeployPackageRuntime({
@@ -86,6 +86,7 @@ export async function deployBackendPackage({
86
86
  failReleaseAttempt,
87
87
  sourceRoot = null,
88
88
  clientName = null,
89
+ successImpl = success,
89
90
  }) {
90
91
  if ((await activePackageRuntime.preflightBackendCapability?.()) === false) return null;
91
92
  const preparedGeneratorVersion = await activePackageRuntime.prepareDeployProjectSource?.({
@@ -163,8 +164,27 @@ export async function deployBackendPackage({
163
164
  }
164
165
 
165
166
  if (!deployed) return null;
167
+ // Persist the production target before finalize. Finalize is a separate durable
168
+ // release effect; if it fails, the next customer retry must reuse this exact
169
+ // app instead of creating a second production child.
170
+ if (envTarget === "prod") {
171
+ writeProjectMetadata({
172
+ appId: deployed.appId,
173
+ displayName,
174
+ envTarget,
175
+ platformUrl: creds.platformUrl,
176
+ gencowJsonPath,
177
+ writeFileSyncImpl,
178
+ });
179
+ }
166
180
  if (releaseAttempt && finalizeReleaseAttempt) {
167
- await finalizeReleaseAttempt({ appId: deployed.appId, attemptId: releaseAttempt.attemptId });
181
+ try {
182
+ await finalizeReleaseAttempt({ appId: deployed.appId, attemptId: releaseAttempt.attemptId });
183
+ } catch (caught) {
184
+ await markAttemptFailed("finalize", "APP_RELEASE_FINALIZE_FAILED");
185
+ throw caught;
186
+ }
187
+ successImpl("App Ready! (release finalized)");
168
188
  }
169
189
 
170
190
  if (!existsSyncImpl(gencowJsonPath)) {
@@ -210,6 +230,7 @@ export async function runStaticDeployFlow({
210
230
  prepareReleaseAttempt,
211
231
  failReleaseAttempt,
212
232
  selectedClient = null,
233
+ successImpl = success,
213
234
  }) {
214
235
  if (!runStaticDeployRuntimeImpl) {
215
236
  errorImpl("Static deploy runtime is not configured.");
@@ -272,6 +293,7 @@ export async function runStaticDeployFlow({
272
293
  failReleaseAttempt,
273
294
  sourceRoot: backendRoot,
274
295
  clientName: selectedClient?.name ?? null,
296
+ successImpl,
275
297
  });
276
298
  if (!deployed) return;
277
299
  activeAppId = deployed.appId;
@@ -438,13 +438,18 @@ export function createStaticDeployRuntime({
438
438
  }
439
439
  let release = null;
440
440
  if (releaseAttemptId && finalizeReleaseAttemptImpl) {
441
- release = await finalizeReleaseAttemptImpl({
442
- creds,
443
- appId,
444
- attemptId: releaseAttemptId,
445
- environment: opts.envTarget || "dev",
446
- platformFetchImpl,
447
- });
441
+ try {
442
+ release = await finalizeReleaseAttemptImpl({
443
+ creds,
444
+ appId,
445
+ attemptId: releaseAttemptId,
446
+ environment: opts.envTarget || "dev",
447
+ platformFetchImpl,
448
+ });
449
+ } catch (caught) {
450
+ await markAttemptFailed("finalize", "APP_RELEASE_FINALIZE_FAILED");
451
+ throw caught;
452
+ }
448
453
  }
449
454
  logImpl("");
450
455
  successImpl("Static deploy complete!");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.213",
3
+ "version": "0.1.215",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,8 +33,8 @@
33
33
  "devDependencies": {
34
34
  "@types/node": "^25.9.5",
35
35
  "better-auth": "^1.6.23",
36
- "@gencow/core": "0.1.43",
37
36
  "@gencow/client": "0.2.7",
37
+ "@gencow/core": "0.1.43",
38
38
  "@gencow/react": "0.2.7"
39
39
  },
40
40
  "scripts": {