@wildo-ai/saas-backend-lib 1.1.8 → 1.1.9
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.
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.d.ts +8 -0
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.d.ts.map +1 -1
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.js +17 -0
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.js.map +1 -1
- package/dist/esm/data-seeding/services/data-seed-application-bootstrap.backend.d.ts.map +1 -1
- package/dist/esm/data-seeding/services/data-seed-application-bootstrap.backend.js +9 -1
- package/dist/esm/data-seeding/services/data-seed-application-bootstrap.backend.js.map +1 -1
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.d.ts +23 -0
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.d.ts.map +1 -0
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.js +25 -0
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.js.map +1 -0
- package/dist/esm/databases/services/postgresql-migration-runner.backend.d.ts +6 -0
- package/dist/esm/databases/services/postgresql-migration-runner.backend.d.ts.map +1 -1
- package/dist/esm/databases/services/postgresql-migration-runner.backend.js +8 -1
- package/dist/esm/databases/services/postgresql-migration-runner.backend.js.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +0 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +4 -39
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts +15 -4
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.js +21 -11
- package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +47 -2
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +56 -8
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
- package/dist/esm/queue/job-consumer.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/job-consumer.backend.service.js +21 -8
- package/dist/esm/queue/job-consumer.backend.service.js.map +1 -1
- package/dist/esm/queue/job-status-tracking.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/job-status-tracking.backend.service.js +4 -1
- package/dist/esm/queue/job-status-tracking.backend.service.js.map +1 -1
- package/dist/esm/queue/queue.backend.service.d.ts +14 -0
- package/dist/esm/queue/queue.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/queue.backend.service.js +68 -12
- package/dist/esm/queue/queue.backend.service.js.map +1 -1
- package/dist/esm/queue/scheduled-evidence-coverage.backend.d.ts +8 -0
- package/dist/esm/queue/scheduled-evidence-coverage.backend.d.ts.map +1 -1
- package/dist/esm/queue/scheduled-evidence-coverage.backend.js +20 -2
- package/dist/esm/queue/scheduled-evidence-coverage.backend.js.map +1 -1
- package/dist/esm/queue/scheduled-execution.backend.service.d.ts +44 -1
- package/dist/esm/queue/scheduled-execution.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/scheduled-execution.backend.service.js +180 -10
- package/dist/esm/queue/scheduled-execution.backend.service.js.map +1 -1
- package/dist/esm/queue/scheduled-message-verifier.backend.service.d.ts +73 -21
- package/dist/esm/queue/scheduled-message-verifier.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/scheduled-message-verifier.backend.service.js +162 -88
- package/dist/esm/queue/scheduled-message-verifier.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.js +16 -1
- package/dist/esm/runtime/services/start-application.backend.service.js.map +1 -1
- package/package.json +10 -10
- package/dist/esm/__tests__/fixture-deep-partial.d.ts +0 -18
- package/dist/esm/__tests__/fixture-deep-partial.d.ts.map +0 -1
- package/dist/esm/__tests__/fixture-deep-partial.js +0 -1
- package/dist/esm/__tests__/fixture-deep-partial.js.map +0 -1
- package/dist/esm/__tests__/integration/redis-integration-gate.d.ts +0 -26
- package/dist/esm/__tests__/integration/redis-integration-gate.d.ts.map +0 -1
- package/dist/esm/__tests__/integration/redis-integration-gate.js +0 -105
- package/dist/esm/__tests__/integration/redis-integration-gate.js.map +0 -1
- package/dist/esm/__tests__/setup.d.ts +0 -7
- package/dist/esm/__tests__/setup.d.ts.map +0 -1
- package/dist/esm/__tests__/setup.js +0 -14
- package/dist/esm/__tests__/setup.js.map +0 -1
- package/dist/esm/__tests__/test-container.d.ts +0 -41
- package/dist/esm/__tests__/test-container.d.ts.map +0 -1
- package/dist/esm/__tests__/test-container.js +0 -98
- package/dist/esm/__tests__/test-container.js.map +0 -1
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.d.ts +0 -47
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.d.ts.map +0 -1
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.js +0 -82
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.js.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.d.ts +0 -57
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.d.ts.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.js +0 -172
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.js.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.d.ts +0 -41
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.d.ts.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.js +0 -54
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.js.map +0 -1
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.d.ts +0 -20
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.d.ts.map +0 -1
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.js +0 -33
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.js.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.d.ts +0 -22
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.d.ts.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.js +0 -92
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.js.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.d.ts +0 -15
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.d.ts.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.js +0 -61
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.js.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.d.ts +0 -21
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.d.ts.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.js +0 -32
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.js.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.d.ts +0 -52
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.d.ts.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.js +0 -74
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.js.map +0 -1
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.d.ts +0 -60
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.d.ts.map +0 -1
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.js +0 -156
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.js.map +0 -1
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.d.ts +0 -31
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.d.ts.map +0 -1
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.js +0 -47
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.js.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.d.ts +0 -20
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.d.ts.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.js +0 -424
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.js.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.d.ts +0 -19
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.d.ts.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.js +0 -49
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.js.map +0 -1
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.d.ts +0 -43
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.d.ts.map +0 -1
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.js +0 -61
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.js.map +0 -1
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.d.ts +0 -25
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.d.ts.map +0 -1
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.js +0 -92
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.js.map +0 -1
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.d.ts +0 -7
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.d.ts.map +0 -1
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.js +0 -194
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.js.map +0 -1
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.d.ts +0 -34
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.d.ts.map +0 -1
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.js +0 -59
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.js.map +0 -1
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.d.ts +0 -48
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.d.ts.map +0 -1
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.js +0 -53
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.js.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.d.ts +0 -3
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.d.ts.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.js +0 -19
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.js.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.d.ts +0 -51
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.d.ts.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.js +0 -24
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.js.map +0 -1
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.d.ts +0 -8
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.d.ts.map +0 -1
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.js +0 -9
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.js.map +0 -1
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.d.ts +0 -10
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.d.ts.map +0 -1
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.js +0 -18
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.js.map +0 -1
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.d.ts +0 -11
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.d.ts.map +0 -1
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.js +0 -34
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.js.map +0 -1
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.d.ts +0 -105
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.d.ts.map +0 -1
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.js +0 -140
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.js.map +0 -1
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.d.ts +0 -35
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.d.ts.map +0 -1
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.js +0 -46
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.js.map +0 -1
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.d.ts +0 -2
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.d.ts.map +0 -1
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.js +0 -18
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.js.map +0 -1
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.d.ts +0 -20
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.d.ts.map +0 -1
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.js +0 -56
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RuntimeEnvironment } from '@wildo-ai/saas-models';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
/**
|
|
3
4
|
* ============================================================================
|
|
@@ -723,6 +724,7 @@ export declare const DataSeedDefinitionSchema: z.ZodObject<{
|
|
|
723
724
|
strategy: z.ZodEnum<typeof DataSeedScopeIterationStrategy>;
|
|
724
725
|
customResolverKey: z.ZodOptional<z.ZodString>;
|
|
725
726
|
}, z.core.$strict>>;
|
|
727
|
+
allowedApplicationEnvironments: z.ZodOptional<z.ZodArray<z.ZodEnum<typeof RuntimeEnvironment>>>;
|
|
726
728
|
}, z.core.$strict>;
|
|
727
729
|
/**
|
|
728
730
|
* Inferred runtime (**output**) shape of {@link DataSeedDefinitionSchema},
|
|
@@ -791,6 +793,12 @@ export type DataSeedDefinitionAuthored = z.input<typeof DataSeedDefinitionSchema
|
|
|
791
793
|
* - `schedule` — optional cron-driven re-run binding; presence is the
|
|
792
794
|
* sole opt-in signal for the engine cron batch. FORBIDDEN for
|
|
793
795
|
* `mode === LAZY`.
|
|
796
|
+
* - `allowedApplicationEnvironments` — optional; omit for reference data the
|
|
797
|
+
* application needs everywhere. DEMONSTRATION data (sample rows that make a
|
|
798
|
+
* fresh application look lived-in) MUST declare it without `production`, e.g.
|
|
799
|
+
* `[RuntimeEnvironment.DEVELOPMENT, RuntimeEnvironment.STAGING]` (import
|
|
800
|
+
* `RuntimeEnvironment` from `@wildo-ai/saas-models`). A restricted seed is never
|
|
801
|
+
* registered elsewhere, and is withheld when the deployment declares no environment.
|
|
794
802
|
*/
|
|
795
803
|
export interface DataSeedDefinitionInput extends DataSeedDefinitionAuthored, DataSeedDefinitionCallbacks {
|
|
796
804
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-seed.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/data-seeding/models/data-seed.shared.schemas.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"data-seed.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/data-seeding/models/data-seed.shared.schemas.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CronExpressionParser } from 'cron-parser';
|
|
2
|
+
import { RuntimeEnvironment } from '@wildo-ai/saas-models';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { validateDataSeedTriggerModeCombination, } from './data-seed-trigger-mode-matrix.shared.utils.js';
|
|
4
5
|
import { describeCaughtError } from '../../errors/utils/error-log-level-classification.backend.utils.js';
|
|
@@ -984,6 +985,22 @@ export const DataSeedDefinitionSchema = z.object({
|
|
|
984
985
|
* adding a field they cannot use at runtime.
|
|
985
986
|
*/
|
|
986
987
|
scopeIteration: DataSeedScopeIterationSchema.optional(),
|
|
988
|
+
/**
|
|
989
|
+
* The runtime environments this seed may run in. OMITTED means every environment — the posture of
|
|
990
|
+
* every reference seed (a role catalog, a closed list, a system template), which the application
|
|
991
|
+
* needs wherever it runs.
|
|
992
|
+
*
|
|
993
|
+
* Declare it to keep DEMONSTRATION data out of production (#2234): a seed that fills a freshly
|
|
994
|
+
* created application with sample customers is wanted in `development` and `staging` and never in
|
|
995
|
+
* `production`, where those rows would sit beside real ones. A restricted seed is withheld at
|
|
996
|
+
* registration, before any trigger, schedule or lazy-access path can reach it, so no dispatch path
|
|
997
|
+
* runs it outside the listed environments.
|
|
998
|
+
*
|
|
999
|
+
* Fails CLOSED: on a deployment that declares no environment, a restricted seed is withheld (an
|
|
1000
|
+
* undeclared environment is never assumed to be development — see `readDeclaredApplicationEnvironment`).
|
|
1001
|
+
* At least one environment: an empty list would declare a seed that can never run.
|
|
1002
|
+
*/
|
|
1003
|
+
allowedApplicationEnvironments: z.array(z.enum(RuntimeEnvironment)).min(1).optional(),
|
|
987
1004
|
}).strict();
|
|
988
1005
|
/** @wildo_source:part:end saas.data-seeding.definition-input */
|
|
989
1006
|
// ---------------------------------------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-seed.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/data-seeding/models/data-seed.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { CronExpressionParser } from 'cron-parser';\nimport { z } from 'zod';\n\nimport {\n validateDataSeedTriggerModeCombination,\n} from './data-seed-trigger-mode-matrix.shared.utils';\nimport { describeCaughtError } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n\n/**\n * ============================================================================\n * Data Seeding — declarative model (`saas-backend-lib/data-seeding/models`)\n * ============================================================================\n *\n * Source-of-truth for the runtime authoring shape of a `DataSeedDefinition`.\n * Application code imports these declarations from\n * `@wildo-ai/saas-backend-lib/data-seeding-models`; the specification layer\n * may add semantic guidance but uses the same runtime contract.\n *\n * Data seeding is backend-only: author declarations in the application backend\n * module and keep browser code free of seed execution concerns. The dedicated\n * public subpath avoids importing the full backend runtime merely to describe\n * a seed.\n *\n * Every public symbol uses the `DataSeed*` prefix so application declarations\n * remain unambiguous beside other bootstrap or agent configuration.\n */\n\n// ---------------------------------------------------------------------------\n// 1. Vocabulary — enums (Task 1.3.1)\n// ---------------------------------------------------------------------------\n\n/** @wildo_source:part:start saas.data-seeding.modes facet:kind:framework-api facet:audience:app-developer */\n/**\n * The four data-seeding modes — the **mode picker** is the single most\n * consequential authoring decision for a `DataSeedDefinition`. Each mode\n * has different write semantics and re-run safety. Pick by answering\n * \"what should happen on the SECOND pass?\":\n *\n * - **`LAZY`** — \"create on first access, never touch again\". Deferred\n * materialization, per-scope idempotency cache. Cannot combine with\n * `schedule?` (periodic re-run contradicts the contract). Write path\n * bypasses the operation pipeline (no hook re-entry from the seed's\n * own write).\n * - **`ADDITIVE`** — \"insert if business key absent; on present rows,\n * re-apply the entry's `data` payload via `upsertByBusinessKey`'s\n * `$set` semantics (no managed-fields gating, no version comparison)\".\n * Operationally additive — the dispatcher never deletes rows or\n * walks orphans for ADDITIVE — but each pass DOES re-stamp the\n * declared payload on every existing row. Use `UPGRADE` instead when\n * you need explicit version-gated mutation with `managedFields`\n * protecting user customization on non-managed columns. Do not describe\n * ADDITIVE as immutable for existing rows: its `upsertByBusinessKey`\n * behavior intentionally reapplies the declared payload.\n * - **`UPGRADE`** — \"insert missing; on present rows whose\n * `seedVersion < declared`, migrate via declared `migrators` and\n * re-apply `managedFields`\". Requires non-empty `managedFields`\n * (otherwise the upgrade has nothing to write). Preserves user\n * customization on non-managed fields.\n * - **`SYNC`** — \"declared set is source of truth: insert missing,\n * update changed managed fields, handle orphans per `onOrphan`\".\n * Requires explicit `onOrphan` policy (D3 — destructive semantics\n * must be declared, never defaulted). The most aggressive mode;\n * reserve for closed enums materialized as rows.\n *\n * Rejected alternatives:\n * - `REPLACE` (full overwrite of every field on every run) — hostile\n * to user customization. `SYNC` with managed-fields semantics covers\n * the same intent safely.\n */\nexport enum DataSeedMode {\n /** Create on first read/access if missing (per scope). Always re-run safe. */\n LAZY = 'lazy',\n /**\n * Insert if business key absent; on present rows, re-apply the\n * declared `entry.data` payload via `upsertByBusinessKey` (`$set`\n * semantics, no managed-fields gating, no version comparison).\n * Operationally additive at the row-set level (no delete, no orphan\n * walk) but NOT immutable per row — see schema-level mode JSDoc\n * (audit L2-6) for the full contract.\n */\n ADDITIVE = 'additive',\n /**\n * Insert if absent; if present and `seedVersion < declared` → migrate via\n * declared migrators (or replace declared-managed fields).\n */\n UPGRADE = 'upgrade',\n /**\n * Full reconciliation: declared set becomes source of truth. Insert missing,\n * update changed, optionally delete orphans (controlled by `onOrphan`).\n */\n SYNC = 'sync',\n}\n/** @wildo_source:part:end saas.data-seeding.modes */\n\n/**\n * Smallest unit at which a seed materializes (plan §3.5).\n *\n * The matching criterion is **always relative to the scope** — e.g. a seed\n * scoped at `ORGANIZATION` with business key `templateKey` matches on\n * `(organizationId, templateKey)`. The dispatcher (Phase 2) composes the\n * compound match from scope + business key automatically, eliminating the\n * \"did I forget the scope filter?\" class of bugs.\n */\nexport enum DataSeedScopeKind {\n /** Once per database. Rare. Trigger: onAppInit. */\n GLOBAL = 'global',\n /** Each APPLICATION row. Trigger: onAppInit. */\n APPLICATION = 'application',\n /** Each ORGANIZATION row. Trigger: organization-resource lifecycle. */\n ORGANIZATION = 'organization',\n /** Each USER row. Trigger: user-resource lifecycle. */\n USER = 'user',\n /** Each row of an arbitrary resource type, named in `relatedResourceType`. */\n CUSTOM = 'custom',\n}\n\n/**\n * Persisted classification on `DataSeedRun.triggerOrigin` (Phase 2 resource).\n *\n * Identifier-style spelling chosen for authoring ergonomics — matches the\n * trigger discriminator strings ('onAppInit', etc.) used in\n * {@link DataSeedTriggerSchema} below. Phase 2 may persist these directly or\n * map them to a kebab-cased external form; that's a persistence concern, not\n * an authoring concern.\n */\nexport enum DataSeedTriggerOrigin {\n APP_INIT = 'onAppInit',\n APP_VERSION_UPGRADE = 'onAppVersionUpgrade',\n RESOURCE_LIFECYCLE = 'resourceLifecycle',\n LAZY_ACCESS = 'lazyAccess',\n /**\n * Reserved placeholder for any non-`schedule.cron`-driven cron\n * primitive (e.g., a future low-level k8s `CronJob` that hits an\n * internal endpoint without going through `DataSeedDefinition.schedule`).\n * As of Phase 7 / Item 47 there is NO production consumer; the\n * declared-`schedule` cron path uses {@link SCHEDULED_RERUN} instead so\n * the audit ledger stays unambiguous about WHERE the cron came from.\n */\n CRON = 'cron',\n ADMIN_MANUAL = 'adminManual',\n /**\n * Phase 7 / Item 47 — fired when the engine-owned cron batch\n * (`data-seeding:scheduled-rerun`) re-dispatches a seed because\n * its declared `DataSeedDefinition.schedule.cron` matched the\n * current tick. Distinct from {@link CRON} (placeholder for\n * non-schedule-driven cron primitives) so dashboards / queries\n * can crisply distinguish the two cron pathways.\n *\n * Consumed by {@link ManualSeedEngineBackendService.runManual}'s\n * optional `originOverride` 3rd parameter (Slice 2). When passed,\n * the per-seed loop policy flips from \"abort on first failure\" to\n * \"continue, capture per-iteration failure\" (N6 / Q5=E2) — admin\n * reruns keep the strict abort semantics because a human is\n * watching, cron reruns must summarize failures rather than stop\n * the world.\n */\n SCHEDULED_RERUN = 'scheduled-rerun',\n}\n\n/**\n * Orphan handling for `DataSeedMode.SYNC` (D3 — explicit per declaration).\n *\n * `SYNC` reconciliation may discover rows in the DB whose business key is no\n * longer in the declared set. There is **no default** policy: the developer\n * must declare what to do, because each option has different operational\n * blast-radius and we refuse to encode the \"least surprising\" behavior on\n * the developer's behalf.\n */\n/**\n * How {@link reconcileDeclaredEntry} decides \"is this entry already applied\" at version equality\n * (PR-30, partial-resource paper). A CLOSED vocabulary with one named source — the external-data run\n * service maps `ExternalDataOrphanPolicy`-style declarations onto it at its seam.\n */\nexport enum DataSeedReconcileStrategy {\n /**\n * The historical contract: the seed VERSION is the change signal; version equality skips.\n * Right for authored seeds, whose entries only change when a developer edits them (and bumps).\n */\n VERSION = 'version',\n /**\n * The pipeline contract: rows change REMOTELY with no version bump, so the declared values are\n * compared against the persisted row AT THE MANAGED PATHS — a difference writes the overlay,\n * equality skips. Values are normalized before comparison (Date ⇄ ISO string), and a declared\n * `undefined` is NOT a difference (A1: the framework never clears from a missing source key).\n */\n CONTENT = 'content',\n}\n\n/**\n * What an `ADDITIVE` pass does to a row that ALREADY exists at the entry's business key.\n *\n * ## Why this is a declaration and not a property of the mode\n *\n * An entry payload is ambiguous in exactly one place, and it is the place that bit: `domains: {}`\n * can mean \"this block is empty\" or \"I declare nothing here — let the schema fill it\". Re-applying\n * the payload is right for the first reading (a seeded catalogue whose declared text must win) and\n * destructive for the second: the engine's own configuration singletons were seeded `ON_APP_INIT`\n * with `{}` blocks, and every boot replaced the verified domains, SSO enforcement and emergency\n * access an administrator had stored (#554). No function of the payload can tell the two apart, so\n * the author says which one they meant.\n *\n * ## Rejected alternatives\n *\n * - **Make ADDITIVE fill-only.** Silently changes every existing seed whose declared values are\n * meant to win on re-run (an application's own webhook-config seed re-stamps its dev payload on\n * purpose), and overlaying a vendor record onto a stored one would keep keys the vendor removed.\n * - **A fifth `DataSeedMode`.** The mode answers \"what happens to the SET of rows on the second\n * pass\"; this answers \"what happens to the FIELDS of one row\". Folding a field-level choice into\n * the row-set vocabulary would need a new trigger × mode matrix column for a distinction the matrix\n * has no opinion on.\n * - **Author the config seeds with no blocks at all.** Then an inserted row carries no block, which\n * is the internal-reader crash #554 was filed for.\n */\nexport enum DataSeedExistingRowTreatment {\n /**\n * The historical `ADDITIVE` contract and the default: the declared payload, materialised, is\n * `$set` onto the existing row on every pass. Right when the declaration is the source of truth\n * for the fields it names.\n */\n REAPPLY_DECLARED_PAYLOAD = 'reapply-declared-payload',\n /**\n * Insert when absent; on an existing row, assign ONLY the leaves the row is missing (see\n * `collectMissingSeedDefaultPaths`) and never replace a stored value. Right for a singleton that\n * the seed creates and a person owns afterwards — configuration rows above all.\n *\n * A seed declaring this on a `RESOURCE_LIFECYCLE` trigger also has its EXISTING rows repaired at\n * boot, once per seed version: such a seed never revisits the rows it created, so without that\n * pass a row written before a default existed would lack it forever.\n */\n FILL_MISSING_DEFAULTS = 'fill-missing-defaults',\n}\n\nexport enum DataSeedSyncOrphanPolicy {\n /** Hard delete the orphan row. Most aggressive. */\n DELETE = 'delete',\n /** Mark the row as deprecated (Phase 2 introduces the marker field). */\n DEPRECATE = 'deprecate',\n /** Do not touch the row; surface the diff in the run report. */\n REPORT = 'report',\n}\n\n// ---------------------------------------------------------------------------\n// 2. Scope reference (Task 1.3.1)\n// ---------------------------------------------------------------------------\n\n/**\n * Discriminated reference to \"where this seed materializes\". The CUSTOM\n * variant is the only one that requires `relatedResourceType` (any CoreResource\n * or app-resource type identifier). The other variants encode the resource\n * implicitly (APPLICATION/ORGANIZATION/USER are core resources, GLOBAL is\n * scope-less).\n */\nexport const DataSeedScopeRefSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal(DataSeedScopeKind.GLOBAL) }).strict(),\n z.object({ kind: z.literal(DataSeedScopeKind.APPLICATION) }).strict(),\n z.object({ kind: z.literal(DataSeedScopeKind.ORGANIZATION) }).strict(),\n z.object({ kind: z.literal(DataSeedScopeKind.USER) }).strict(),\n z\n .object({\n kind: z.literal(DataSeedScopeKind.CUSTOM),\n /**\n * The resource type whose rows define an instance of this scope. The\n * dispatcher (Phase 2) materializes the seed for every existing row of\n * this type (and again on each new row via the resource lifecycle hook).\n */\n relatedResourceType: z.string(),\n })\n .strict(),\n]);\n\nexport type DataSeedScopeRef = z.infer<typeof DataSeedScopeRefSchema>;\n\n/**\n * Cross-seed reference. Phase 1 only stores the symbolic name; Phase 2 adds\n * a `SeedReferenceResolver` that maps `seed.ref(seedId)` → matched row `_id`\n * at dispatch time, so renaming a business key never breaks foreign keys\n * (plan §3.3, §4.6).\n */\nexport const DataSeedRefSchema = z.object({\n seedKey: z.string(),\n});\n\nexport type DataSeedRef = z.infer<typeof DataSeedRefSchema>;\n\n// ---------------------------------------------------------------------------\n// 3. Trigger schema (Task 1.3.2)\n// ---------------------------------------------------------------------------\n\n/**\n * Resource lifecycle phase that fires a Level-B seed (plan §3.1).\n */\nexport enum DataSeedResourceLifecyclePhase {\n BEFORE_CREATE = 'beforeCreate',\n AFTER_CREATE = 'afterCreate',\n BEFORE_UPDATE = 'beforeUpdate',\n AFTER_UPDATE = 'afterUpdate',\n BEFORE_DELETE = 'beforeDelete',\n AFTER_DELETE = 'afterDelete',\n}\n\n/**\n * Discriminator constants used by {@link DataSeedTriggerSchema}. Exported so\n * downstream code (matrix validator, dispatcher) can use the same literals\n * as the schema.\n */\nexport enum DataSeedTriggerKind {\n ON_APP_INIT = 'onAppInit',\n ON_APP_VERSION_UPGRADE = 'onAppVersionUpgrade',\n ON_SCHEDULED_REFRESH = 'onScheduledRefresh',\n ON_MANUAL_TRIGGER = 'onManualTrigger',\n RESOURCE_LIFECYCLE = 'resourceLifecycle',\n LAZY_ACCESS = 'lazyAccess',\n}\n\n/**\n * Trigger discriminated union (plan §3.1).\n *\n * The four payload-less variants are **Level A** (application lifecycle).\n * `resourceLifecycle` and `lazyAccess` are **Level B** (per-row). The\n * segregation is enforced by {@link validateDataSeedTriggerModeCombination}\n * at factory time — see plan §3.6 for the allowed cells.\n */\nexport const DataSeedTriggerSchema = z.discriminatedUnion('triggerKind', [\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_APP_INIT) }).strict(),\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_APP_VERSION_UPGRADE) }).strict(),\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_SCHEDULED_REFRESH) }).strict(),\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_MANUAL_TRIGGER) }).strict(),\n z.object({\n triggerKind: z.literal(DataSeedTriggerKind.RESOURCE_LIFECYCLE),\n /** The resource type whose lifecycle event fires this seed. */\n resourceType: z.string(),\n /** Phase of the resource operation that fires the seed. */\n phase: z.enum(DataSeedResourceLifecyclePhase),\n })\n .strict(),\n z.object({\n triggerKind: z.literal(DataSeedTriggerKind.LAZY_ACCESS),\n /**\n * The resource read by the user that triggers materialization.\n *\n * Phase 5 audit F-2/F-12/F-18: this field is **auto-injected by the\n * factory** from `seed.lazyAccess.onResource`. Authors MUST NOT\n * declare a `LAZY_ACCESS` trigger explicitly — declare the\n * `lazyAccess` config block instead and the factory will materialize\n * a single matching trigger here. Single source of truth.\n */\n resourceType: z.string(),\n /**\n * The operation (`LIST`, `READ`, …) whose first call triggers the\n * seed. Auto-injected from `seed.lazyAccess.onOperation`.\n */\n operation: z.string(),\n // No `matchScope` — `seed.scope.kind` is the single source of truth\n // for the scope at which \"first access\" is evaluated. The previous\n // duplicate field on this trigger and on `lazyAccess` could silently\n // diverge from `seed.scope.kind`; the dispatcher always consumed\n // `seed.scope.kind`, so the duplicates were dead-write surface\n // for divergence bugs.\n })\n .strict(),\n]);\n\nexport type DataSeedTrigger = z.infer<typeof DataSeedTriggerSchema>;\n\n/**\n * True iff the trigger is one of the Level-A application lifecycle variants.\n * Re-exported here so the factory and the matrix validator share one\n * classification function.\n */\nexport function isLevelADataSeedTrigger(trigger: DataSeedTrigger): boolean {\n return (\n trigger.triggerKind === DataSeedTriggerKind.ON_APP_INIT\n || trigger.triggerKind === DataSeedTriggerKind.ON_APP_VERSION_UPGRADE\n || trigger.triggerKind === DataSeedTriggerKind.ON_SCHEDULED_REFRESH\n || trigger.triggerKind === DataSeedTriggerKind.ON_MANUAL_TRIGGER\n );\n}\n\n/**\n * True iff the trigger is one of the Level-B per-row variants\n * (`resourceLifecycle` or `lazyAccess`). Symmetric with\n * {@link isLevelADataSeedTrigger}; provided so call sites read as\n * `isLevelBDataSeedTrigger(t)` rather than `!isLevelADataSeedTrigger(t)`.\n *\n * Implemented by negating the Level-A check rather than re-listing the\n * Level-B kinds: keeping the two helpers tied means adding a new Level-A\n * trigger automatically extends both predicates without a coverage hole.\n */\nexport function isLevelBDataSeedTrigger(trigger: DataSeedTrigger): boolean {\n return !isLevelADataSeedTrigger(trigger);\n}\n\n// ---------------------------------------------------------------------------\n// 4. Entries (static + computed) (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * A single static seed row authored at declaration time.\n *\n * `businessKey` is an arbitrary record matching either a single\n * `isBusinessKey()`-marked field or a composite `businessKeySet([...])` on\n * the target resource. The dispatcher (Phase 2) validates shape against the\n * resource's schema; Phase 1 keeps it permissive.\n *\n * `data` is the row payload (excluding `_id`, which is generated by the\n * dispatcher when the row is inserted, and `__seed` which is system-managed).\n *\n * **Deferred validation — L1-4 (post-Phase-7 close-out):** the\n * factory does NOT cross-check that the keys present on\n * `businessKey` match the `isBusinessKey()` / `businessKeySet([...])`\n * declarations on the target resource's Zod schema. The check\n * REQUIRES read access to the resource registry, which is\n * intentionally not wired into the seed factory (`createDataSeedDefinition`\n * runs at module-init time, BEFORE the resource registry is fully\n * populated — wiring it would force a load-order coupling and risk\n * a circular import between `data-seeding/models` and\n * `resources/services`). The dispatcher performs the equivalent\n * runtime check on the first pass: a mismatched `businessKey`\n * produces a \"businessKey shape does not match resource declaration\"\n * failure surfaced through the standard `DataSeedRun.failureReport`\n * channel. Wiring a registry-lookup at factory time is left as a\n * future enhancement (would need a deferred-validation queue\n * flushed after the registry's bootstrap step — see plan §3.4\n * bootstrap chain), but the runtime path is sufficient because\n * authors run the dispatcher in CI on every change.\n */\nexport const DataSeedStaticEntrySchema = z.object({\n businessKey: z.record(z.string(), z.unknown()),\n data: z.record(z.string(), z.unknown()),\n}).strict();\n\nexport type DataSeedStaticEntry = z.infer<typeof DataSeedStaticEntrySchema>;\n\n/**\n * Function shape of a **computed** seed entry — used by Level B seeds whose\n * payload depends on the triggering row (e.g. an `afterCreate` seed needs\n * the `_id` of the row that triggered it). Captured outside the Zod schema\n * because Zod cannot validate function return types at parse time.\n *\n * `triggeringRow` is opaque (`unknown`) at this layer — Phase 2's typed\n * dispatcher narrows it to the resource's row type at the call site.\n */\nexport type DataSeedComputedEntryFn = (context: {\n /**\n * The resource row whose lifecycle event fired the seed (Level B only).\n * Always present for `triggerKind: 'resourceLifecycle'`. `null` for lazy\n * access seeds where there is no triggering row.\n */\n triggeringRow: { _id: string } & Record<string, unknown> | null;\n /**\n * Read-only execution context: tunneled audit attribution only. Seeds\n * always run as `INTERNALLY_INITIATED_REQUEST` regardless (D4) — this\n * field is for capturing `triggeredByUserId` into the row's `__seed`\n * ledger and the `DataSeedRun` record.\n */\n executionContext: {\n triggeredByUserId: string | null;\n };\n}) => DataSeedStaticEntry | Promise<DataSeedStaticEntry>;\n\n// ---------------------------------------------------------------------------\n// 5. Migrators + renames (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Migrator function for `DataSeedMode.UPGRADE`. Receives the persisted row\n * (cast to `Record<string, unknown>` at this layer; Phase 2 narrows) and\n * returns the upgraded form. The dispatcher applies migrators sequentially\n * starting from the persisted version's migrator and walking up to the\n * declared version, before then re-applying the declared `managedFields`.\n */\nexport type DataSeedMigratorFn = (row: Record<string, unknown>) =>\n Record<string, unknown>\n | Promise<Record<string, unknown>>;\n\n/**\n * Business-key rename declaration. The dispatcher uses these to find a row\n * by its **previous** business-key value and update it to the new value\n * before reconciliation runs — preserving identity across renames.\n */\nexport const DataSeedRenameSchema = z.object({\n field: z.string(),\n from: z.string(),\n to: z.string(),\n}).strict();\n\nexport type DataSeedRename = z.infer<typeof DataSeedRenameSchema>;\n\n/**\n * Lazy-access binding — the **single declaration site** for a LAZY seed's\n * trigger. Required when (and only when) `mode === DataSeedMode.LAZY`.\n *\n * Phase 5 audit F-2/F-12/F-18 consolidated three former declaration sites\n * into one: previously `(resourceType, operation, matchScope)` could be\n * declared on `seed.lazyAccess`, on `seed.triggers[*]` (a `LAZY_ACCESS`\n * trigger), AND `matchScope` also overlapped with `seed.scope.kind`.\n * Three sites for the same data → silent-divergence bugs. Today:\n *\n * - `(onResource, onOperation)` live ONLY here. The factory\n * auto-injects a matching `LAZY_ACCESS` trigger into `seed.triggers`\n * so the registry's by-trigger-kind index keeps working without\n * authors having to re-declare the same data.\n * - The \"scope at which first access is evaluated\" is `seed.scope.kind`\n * (was previously also called `matchScope` here — the field was\n * removed because the dispatcher only ever consulted `seed.scope.kind`,\n * making the local copy a dead-write surface for divergence bugs).\n *\n * Phase 5 (LAZY engine) decisions baked into this contract:\n *\n * **P5-E (E2 — any operation allowed)**: `onOperation` is intentionally a\n * free-form string with NO factory-time allowlist. The canonical\n * use-cases are READ + LIST (\"default dashboard appears the first time\n * a user opens the dashboard list\"), but mutations (CREATE/UPDATE/DELETE)\n * are also legal — the lazy guard registration in\n * {@link wireLazyAccessGuards} simply hooks whichever operation the seed\n * declares. Self-recursion is safe because seed bodies write via the\n * REPOSITORY-layer `repo.upsertByBusinessKey` (bypasses the operation\n * pipeline → no hook re-entry from the seed's own write). Re-entrancy\n * hardening at {@link DataSeedDispatcherBackendService.runLazy}\n * additionally short-circuits when `parentCtx.executionType ===\n * INTERNALLY_INITIATED_REQUEST` or `parentCtx.subCallDepth > 0`.\n *\n * **P5-B (B2 — all 5 scopes supported)**: `seed.scope.kind` may be any\n * of GLOBAL / APPLICATION / USER / ORGANIZATION / CUSTOM. Resolution to\n * a concrete `relatedId` happens at dispatch time inside\n * `resolveLazyMatchScopeAnchor`:\n * - GLOBAL → `null`\n * - APPLICATION → `parentCtx.applicationId` (CONFIGURATION error if undefined)\n * - USER → `parentCtx.userId` (CONFIGURATION error if undefined — anonymous callers cannot trigger user-scoped lazy seeds)\n * - ORGANIZATION → `parentCtx.organizationId` (CONFIGURATION error if undefined)\n * - CUSTOM → first `parentCtx.relationshipContexts[*]` whose `resourceType === seed.scope.relatedResourceType` (CONFIGURATION error if absent)\n *\n * **P5-C (C1 — no version-gate re-run)**: LAZY is \"first access creates\n * the row, never re-applied\". The dispatcher checks for ANY existing\n * `DataSeedRun` row with `status: COMPLETED` for\n * `(seedKey, scopeKind, relatedResourceType, relatedId)` regardless of\n * `seed.version`. If you need re-apply-on-version-bump semantics,\n * declare an UPGRADE seed instead.\n */\nexport const DataSeedLazyAccessConfigSchema = z.object({\n /** The resource whose access triggers the seed. */\n onResource: z.string(),\n /**\n * The operation whose first call triggers the seed. Canonical:\n * READ / LIST. Mutations are technically allowed (P5-E=E2) but\n * discouraged for non-system seeds because the user-facing operation\n * may semantically conflict with the framework-initiated row write.\n */\n onOperation: z.string(),\n}).strict();\n\nexport type DataSeedLazyAccessConfig = z.infer<typeof DataSeedLazyAccessConfigSchema>;\n\n// ---------------------------------------------------------------------------\n// 5.5. Schedule + scope iteration (Phase 7 — Item 47, cron scheduler)\n// ---------------------------------------------------------------------------\n\n/** @wildo_source:part:start saas.data-seeding.scope-iteration-strategy facet:kind:framework-api facet:audience:app-developer */\n/**\n * The five iteration strategies a scheduled seed may declare. Together they\n * cover every {@link DataSeedScopeKind} without making scope enumeration an\n * implicit, unreviewed behavior.\n *\n * The first four are framework-resolved (the engine paginates the\n * canonical resource for that scope kind via the `iterateAllOfResource`\n * shared helper). `CUSTOM_RESOLVER` defers scope enumeration to a\n * developer-supplied function registered under\n * {@link DataSeedScheduleSchema.shape.customResolverKey} — required when\n * `scope.kind === CUSTOM` and available as an opt-in for the three\n * resource-aligned scopes (e.g., \"all applications matching this\n * filter\", \"users in this org bucket\").\n *\n * This is deliberately separate from {@link DataSeedScopeKind}: operator\n * intent and scope kind are orthogonal concerns. Auto-deriving the strategy\n * from `scope.kind` would foreclose the valid use case \"I\n * want a curated subset of applications, not all of them\" (which\n * `CUSTOM_RESOLVER` covers cleanly even on an `APPLICATION`-scoped\n * seed). Keeping them separate lets an application refine its target audience\n * without changing the seed's data ownership contract.\n */\nexport enum DataSeedScopeIterationStrategy {\n /** Single dispatch with `scope: { kind: GLOBAL }`. Only legal for `scope.kind === GLOBAL`. */\n GLOBAL = 'global',\n /** Paginate every row of the `applications` core resource, dispatch once per `_id`. */\n ALL_APPLICATIONS = 'all-applications',\n /** Paginate every row of the `organizations` core resource, dispatch once per `_id`. */\n ALL_ORGANIZATIONS = 'all-organizations',\n /** Paginate every row of the `users` core resource, dispatch once per `_id`. */\n ALL_USERS = 'all-users',\n /** Defer scope enumeration to a developer-registered resolver (see `customResolverKey`). */\n CUSTOM_RESOLVER = 'custom-resolver',\n}\n/** @wildo_source:part:end saas.data-seeding.scope-iteration-strategy */\n\n/** @wildo_source:part:start saas.data-seeding.schedule facet:kind:framework-api facet:audience:app-developer */\n/**\n * Schedule + scope-iteration block on a {@link DataSeedDefinition}.\n *\n * Presence of this object is the sole opt-in signal for scheduled re-runs.\n * Seeds without `schedule?` are never selected by the cron path; they run only\n * through their declared lifecycle, lazy-access or operator-triggered paths.\n *\n * **Field-level invariants enforced by the factory** (not by Zod alone):\n *\n * - `cron` — parsed by `cron-parser` at factory time\n * ({@link validateDataSeedScheduleCronExpression}); reject malformed\n * expressions early so app startup fails loudly instead of the next\n * tick silently logging a parse error.\n * - `iterationStrategy` — must satisfy the alignment matrix in\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}, enforced by\n * {@link validateDataSeedScheduleAgainstScope}.\n * - `customResolverKey` — REQUIRED iff\n * `iterationStrategy === CUSTOM_RESOLVER`; FORBIDDEN otherwise\n * (enforced inline by the schema's `superRefine`).\n * - **Mode** — `mode === LAZY` + `schedule` is rejected at factory\n * time (a LAZY seed's contract is \"first access creates the row,\n * never re-applied\" — periodic re-runs contradict that). Enforced by\n * {@link validateModeSpecificFields}.\n *\n * `customResolverKey` is a string lookup against the application-registered\n * resolver. The definition never carries a function directly: keeping it\n * declarative lets the registry and companion inspect the schedule safely.\n */\nexport const DataSeedScheduleSchema = z\n .object({\n /**\n * Standard 5-field crontab expression (e.g. `'0 * * * *'` for\n * hourly). Validated at factory time via `cron-parser`; a malformed\n * expression throws with the seed key in the message so app\n * startup surfaces it immediately.\n */\n cron: z.string().min(1),\n /**\n * Which scope tuples this seed re-runs over per cron tick. Must\n * align with the seed's declared `scope.kind` per\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}.\n */\n iterationStrategy: z.enum(DataSeedScopeIterationStrategy),\n /**\n * String key looked up against the runtime\n * `DataSeedScopeResolverRegistryBackendService`. Resolvers are\n * registered at app startup via\n * `BackendDomainModule.dataSeedScopeResolvers` (aggregated by\n * `mergeBackendDomainModules` and threaded through\n * `ApplicationInitializationConfig.dataSeedScopeResolvers`).\n * REQUIRED when `iterationStrategy === CUSTOM_RESOLVER`,\n * FORBIDDEN otherwise.\n */\n customResolverKey: z.string().min(1).optional(),\n })\n .strict()\n .superRefine((value, ctx) => {\n const requiresResolver\n = value.iterationStrategy === DataSeedScopeIterationStrategy.CUSTOM_RESOLVER;\n const hasResolver = value.customResolverKey !== undefined;\n if (requiresResolver && !hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is required when iterationStrategy === CUSTOM_RESOLVER (the resolver returns the explicit list of relatedIds to dispatch).',\n });\n }\n if (!requiresResolver && hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is only valid when iterationStrategy === CUSTOM_RESOLVER (other strategies are framework-resolved and would silently ignore the resolver).',\n });\n }\n });\n\nexport type DataSeedSchedule = z.infer<typeof DataSeedScheduleSchema>;\n/** @wildo_source:part:end saas.data-seeding.schedule */\n\n/**\n * Strategy ↔ scope-kind alignment matrix. Single source of truth shared\n * by:\n *\n * 1. {@link validateDataSeedScheduleAgainstScope} — factory-time\n * rejection of misaligned declarations (fail-fast at app startup).\n * 2. The future `iterateAllOfResource` shared helper (Slice 4) —\n * runtime sanity assert that the seed's resolved strategy still\n * matches its declared scope (defense-in-depth — a divergence\n * here would mean a registry mutation slipped past the factory).\n *\n * The matrix is intentionally exhaustive (every {@link DataSeedScopeKind}\n * key present): TypeScript exhaustiveness checking on the Record type\n * forces us to add an entry whenever a new scope kind ships. Empty\n * arrays would be a code smell — every scope kind must have AT LEAST\n * one legal iteration strategy or cron-aware seeds at that scope kind\n * are unreachable.\n */\nexport const ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND: Readonly<\n Record<DataSeedScopeKind, ReadonlyArray<DataSeedScopeIterationStrategy>>\n> = {\n [DataSeedScopeKind.GLOBAL]: [DataSeedScopeIterationStrategy.GLOBAL],\n [DataSeedScopeKind.APPLICATION]: [\n DataSeedScopeIterationStrategy.ALL_APPLICATIONS,\n DataSeedScopeIterationStrategy.CUSTOM_RESOLVER,\n ],\n [DataSeedScopeKind.ORGANIZATION]: [\n DataSeedScopeIterationStrategy.ALL_ORGANIZATIONS,\n DataSeedScopeIterationStrategy.CUSTOM_RESOLVER,\n ],\n [DataSeedScopeKind.USER]: [\n DataSeedScopeIterationStrategy.ALL_USERS,\n DataSeedScopeIterationStrategy.CUSTOM_RESOLVER,\n ],\n [DataSeedScopeKind.CUSTOM]: [DataSeedScopeIterationStrategy.CUSTOM_RESOLVER],\n};\n\n/**\n * Throw with an actionable error when `iterationStrategy` is not legal\n * for `scopeKind`. Called from {@link createDataSeedDefinition} after\n * the structural parse + scope/trigger checks have run.\n *\n * Why a separate function (not a `.superRefine` on\n * {@link DataSeedScheduleSchema}): the schedule schema is decoupled\n * from the scope-kind context — only the factory has both pieces in\n * scope. Keeping them separated avoids `superRefine` chains that\n * couple two otherwise-orthogonal authoring surfaces.\n */\nexport function validateDataSeedScheduleAgainstScope(\n seedKey: string,\n scopeKind: DataSeedScopeKind,\n schedule: DataSeedSchedule,\n): void {\n const allowed = ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND[scopeKind];\n if (!allowed.includes(schedule.iterationStrategy)) {\n throw new Error(\n `[DataSeed:${seedKey}] schedule.iterationStrategy='${schedule.iterationStrategy}' is not legal for scope.kind='${scopeKind}'. Allowed values: [${allowed.join(', ')}].`,\n );\n }\n}\n\n/**\n * Top-level scope-iteration declaration block (Phase 7 / Item 51 —\n * Slice 1, future-readiness).\n *\n * **Status as of 2026-04-17 — schema surface only**: this block is\n * accepted by the factory and validated against the seed's `scope.kind`\n * + the `ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND` matrix, but the\n * runtime Level-A non-cron path\n * ({@link DataSeedDispatcherBackendService.runApplicationLifecycle})\n * still throws `NotImplemented` for ORG/USER/CUSTOM-scoped seeds\n * (`assertSeedIsSupportedForLevelA`). Authors declaring this block\n * today will hit that runtime gate; the schema is in place so the day\n * the engine wiring lands (Item 51 Slices 2–5, deferred until a real\n * consumer surfaces — see\n * `.cursor/plans/seeding-phase-7-multitenant-upgrade-sync.md`) the\n * declaration site is already canonical and the cron batch's read\n * site can switch from {@link DataSeedSchedule.iterationStrategy} to\n * {@link DataSeedScopeIteration.strategy} in a single targeted edit.\n *\n * **Why a separate top-level block** (not a derived view of\n * `schedule`): the cron path (Item 47) and the non-cron Level-A\n * lifecycle path (Item 51) iterate the same fan-out shape but are\n * triggered by orthogonal mechanisms. Coupling iteration intent to\n * `schedule` would force every multi-tenant Level-A seed to declare\n * an unwanted cron just to express \"iterate per-row at startup\". A\n * top-level field carries the iteration intent independently of WHEN\n * the iteration runs.\n *\n * **Drift guard** (load-bearing during the dual-declaration window):\n * when both `seed.scopeIteration` AND `seed.schedule.iterationStrategy`\n * are present, the factory rejects mismatched declarations\n * ({@link validateScopeIterationVsScheduleConsistency}). This is what\n * lets Item 47's cron batch keep reading `schedule.iterationStrategy`\n * without risk of silent divergence; once Item 51's engine wiring\n * lands the cron read site can be switched in confidence.\n *\n * **Inline `superRefine`** (`customResolverKey` REQUIRED iff\n * `strategy === CUSTOM_RESOLVER`) mirrors {@link DataSeedScheduleSchema}\n * — same vocabulary, same gate, same error wording so authors who\n * have learned one block carry that knowledge to the other.\n */\nexport const DataSeedScopeIterationSchema = z\n .object({\n /**\n * Which scope tuples this seed iterates over per non-cron Level-A\n * dispatch. Must align with the seed's declared `scope.kind` per\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}; the factory\n * additionally rejects this whole block on GLOBAL/APPLICATION-scoped\n * seeds (single-anchor at runtime, no iteration possible — see\n * {@link validateScopeIterationAgainstScope}).\n */\n strategy: z.enum(DataSeedScopeIterationStrategy),\n /**\n * String key looked up against the runtime\n * `DataSeedScopeResolverRegistryBackendService` (Item 47 / Slice 3)\n * when `strategy === CUSTOM_RESOLVER`. REQUIRED in that case,\n * FORBIDDEN otherwise — same contract as\n * {@link DataSeedScheduleSchema.shape.customResolverKey}.\n */\n customResolverKey: z.string().min(1).optional(),\n })\n .strict()\n .superRefine((value, ctx) => {\n const requiresResolver\n = value.strategy === DataSeedScopeIterationStrategy.CUSTOM_RESOLVER;\n const hasResolver = value.customResolverKey !== undefined;\n if (requiresResolver && !hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is required when strategy === CUSTOM_RESOLVER (the resolver returns the explicit list of relatedIds to dispatch).',\n });\n }\n if (!requiresResolver && hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is only valid when strategy === CUSTOM_RESOLVER (other strategies are framework-resolved and would silently ignore the resolver).',\n });\n }\n });\n\nexport type DataSeedScopeIteration = z.infer<typeof DataSeedScopeIterationSchema>;\n\n/**\n * Throw with an actionable error when `scopeIteration` is declared on\n * a single-anchor scope (GLOBAL/APPLICATION) — these scopes resolve to\n * exactly one anchor at runtime\n * (`resolveLevelAScopeAnchor`/`resolveLevelBScopeAnchor`), so iterating\n * is meaningless. Then enforce the strategy-vs-scope alignment matrix\n * for the remaining ORG/USER/CUSTOM scopes via the existing\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND} matrix (single\n * source of truth shared with the cron path).\n *\n * Why a separate function (not a `.superRefine` on\n * {@link DataSeedScopeIterationSchema}): same reasoning as\n * {@link validateDataSeedScheduleAgainstScope} — the iteration schema\n * is decoupled from the scope-kind context; only the factory has both\n * pieces in scope. Keeping them separated avoids `superRefine` chains\n * that couple two otherwise-orthogonal authoring surfaces.\n */\nexport function validateScopeIterationAgainstScope(\n seedKey: string,\n scopeKind: DataSeedScopeKind,\n scopeIteration: DataSeedScopeIteration,\n): void {\n if (\n scopeKind === DataSeedScopeKind.GLOBAL\n || scopeKind === DataSeedScopeKind.APPLICATION\n ) {\n throw new Error(\n `[DataSeed:${seedKey}] scope.kind='${scopeKind}' resolves to a single anchor at runtime; declaring \\`scopeIteration\\` here is meaningless (there is nothing to iterate). Remove the \\`scopeIteration\\` block — it is only valid for ORGANIZATION/USER/CUSTOM-scoped seeds where multiple per-row anchors exist.`,\n );\n }\n const allowed = ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND[scopeKind];\n if (!allowed.includes(scopeIteration.strategy)) {\n throw new Error(\n `[DataSeed:${seedKey}] scopeIteration.strategy='${scopeIteration.strategy}' is not legal for scope.kind='${scopeKind}'. Allowed values: [${allowed.join(', ')}].`,\n );\n }\n}\n\n/**\n * Drift guard: when BOTH `scopeIteration` AND `schedule` are declared\n * on the same seed, their iteration intent MUST match — the cron path\n * (Item 47) reads from `schedule.iterationStrategy` today; the\n * non-cron Level-A path (Item 51, future) will read from\n * `scopeIteration.strategy`. If the two declarations disagreed, the\n * cron tick and the lifecycle dispatch would fan out the same seed\n * through different anchor sets — a silent divergence the framework\n * cannot recover from at runtime.\n *\n * This check is what allows Item 51 Slice 1 to ship a future-ready\n * schema surface WITHOUT touching Item 47's cron batch read site:\n * authors who declare both blocks must keep them in agreement, so\n * once Item 51 Slice 2 lands the cron batch can switch its read site\n * to the new field with no behavioral change.\n */\nexport function validateScopeIterationVsScheduleConsistency(\n seedKey: string,\n scopeIteration: DataSeedScopeIteration,\n schedule: DataSeedSchedule,\n): void {\n if (scopeIteration.strategy !== schedule.iterationStrategy) {\n throw new Error(\n `[DataSeed:${seedKey}] scopeIteration.strategy='${scopeIteration.strategy}' disagrees with schedule.iterationStrategy='${schedule.iterationStrategy}'. The two declaration sites describe the same scope-fan-out intent and MUST match — the cron path and the lifecycle path cannot iterate the same seed via different strategies.`,\n );\n }\n if (scopeIteration.customResolverKey !== schedule.customResolverKey) {\n throw new Error(\n `[DataSeed:${seedKey}] scopeIteration.customResolverKey='${scopeIteration.customResolverKey ?? 'undefined'}' disagrees with schedule.customResolverKey='${schedule.customResolverKey ?? 'undefined'}'. When both declarations are present they MUST resolve to the same registered resolver — the cron path and the lifecycle path cannot iterate the same seed via different resolvers.`,\n );\n }\n}\n\n/**\n * Validate `schedule.cron` parses cleanly at factory time. Wraps the\n * `cron-parser` throw so the error message names the failing seed —\n * otherwise the bare parser error names the malformed expression but\n * not which seed declared it, forcing operators to grep.\n *\n * `cron-parser` 5.x throws on invalid input; we re-throw with the\n * seedKey context. We do NOT cache the parsed expression here — the\n * cron batch (Slice 5) re-parses per tick because the parser carries\n * mutable iteration state (`next()`, `prev()`) that we don't want to\n * leak between ticks.\n */\nexport function validateDataSeedScheduleCronExpression(\n seedKey: string,\n cron: string,\n): void {\n try {\n CronExpressionParser.parse(cron);\n }\n catch (err) {\n const message = describeCaughtError(err);\n throw new Error(\n `[DataSeed:${seedKey}] schedule.cron='${cron}' is not a valid cron expression: ${message}`,\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// 6. Definition schema (Zod-validated structural shape) (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Structural schema for a {@link DataSeedDefinition}. Used by the factory\n * for first-line parse validation; mode-specific cross-field rules and the\n * §3.6 trigger × mode matrix are enforced by the factory **after** the\n * structural parse (see {@link createDataSeedDefinition}).\n */\nexport const DataSeedDefinitionSchema = z.object({\n /**\n * Stable identifier of this seed across deploys. Convention: kebab-case\n * segments joined by `:`, e.g. `\"system-emails:welcome\"`. Documented but\n * not regex-enforced in v1 — Phase 2 may add a lint.\n */\n seedKey: z.string().min(1),\n\n /** Where this seed materializes. */\n scope: DataSeedScopeRefSchema,\n\n /** The target resource whose rows the seed produces / updates. */\n resourceType: z.string().min(1),\n\n /**\n * Triggers that fire this seed. Required and must satisfy the §3.6 matrix\n * (relative to `mode`) for every mode EXCEPT `LAZY`.\n *\n * For `mode === LAZY`, authors MUST omit this field — the factory\n * auto-injects exactly one `LAZY_ACCESS` trigger from the\n * {@link DataSeedLazyAccessConfigSchema} block (Phase 5 audit\n * F-2/F-12/F-18: single declaration site for lazy-access wiring).\n * Declaring `triggers` explicitly for a LAZY seed is rejected at\n * factory time with an actionable error.\n */\n triggers: z.array(DataSeedTriggerSchema).optional(),\n\n /** Reconciliation mode (see {@link DataSeedMode}). */\n mode: z.enum(DataSeedMode),\n\n /**\n * Per-seed monotonic version. Bumping this is the developer's signal to\n * the dispatcher: \"re-run me on existing scopes to apply changes\". The\n * `DataSeedAppliedVersionsSummary` cache (Phase 2) makes the version-gate\n * check O(1) at boot.\n */\n version: z.number().int().positive(),\n\n /**\n * Dotted-path field names the seed owns.\n *\n * Optional to write, defaulting to `[]`. Because the authoring type is\n * derived from the schema's INPUT shape ({@link DataSeedDefinitionAuthored})\n * while consumers read the OUTPUT shape, authors may omit this field and the\n * dispatcher still reads a guaranteed `string[]`.\n *\n * Declare it when `mode in {UPGRADE, SYNC}` — `UPGRADE` rejects an empty list\n * at factory time, since an upgrade with nothing to re-apply is a no-op.\n *\n * OMIT it for `LAZY` and `ADDITIVE`. Although every `ADDITIVE` pass re-stamps\n * the declared payload onto existing rows (audit L2-6 — see\n * {@link DataSeedMode} JSDoc), the writer does NOT consult `managedFields` on\n * that mode: the full declared payload is the source of truth. Naming fields\n * there would imply an ownership boundary the dispatcher does not enforce.\n * Use `UPGRADE` if you need version-gated re-stamping that preserves user\n * customization on non-managed columns.\n */\n managedFields: z.array(z.string()).default([]),\n\n /**\n * Static entries (mutually exclusive with `entry` — enforced by factory).\n * Pre-known set of rows; typical for Level-A `ADDITIVE` / `UPGRADE` /\n * `SYNC` seeds (system templates, role catalog, country codes, …).\n */\n entries: z.array(DataSeedStaticEntrySchema).optional(),\n\n /**\n * Opt-in: inject the resolved scope foreign key into every STATIC entry's\n * `businessKey` and `data` at dispatch time.\n *\n * Level-A (application-lifecycle) seeds have no triggering row, so a\n * scope-anchored singleton (e.g. `applicationSsoConfig`, whose FK column is\n * `applicationId`) cannot hard-code its owner's `_id` at declaration time the\n * way a Level-B `entry(triggeringRow)` can. Setting this flag makes the\n * dispatcher materialise the scope FK (from the canonical\n * `coreResourceShared_FieldIdentifier` map, e.g. APPLICATION → `applicationId`)\n * onto each entry — the symmetric analogue of a Level-B entry hand-writing its\n * FK. Authors then leave the FK OUT of the static `businessKey`/`data`.\n *\n * OFF by default: existing Level-A static seeds (e.g. a system-email-template\n * catalog keyed by `templateKey`, not by the app FK) are unaffected. FORBIDDEN\n * for `scope.kind === GLOBAL` (no owning scope → no FK) — rejected at factory\n * time.\n *\n * `.optional()` (not `.default(false)`) so consumers can distinguish \"never\n * declared\" from an explicit `false`; they treat `undefined` as `false`.\n *\n * Historical note: this was originally `.optional()` to dodge a defect in the\n * authoring type — `DataSeedDefinitionInput` was derived from\n * `DataSeedDefinitionParsed` (`z.infer`, the OUTPUT shape), where a `.default()`\n * field becomes REQUIRED on the author input. That defect is fixed: the\n * authoring type is now {@link DataSeedDefinitionAuthored} (`z.input`), so\n * `.default(...)` no longer forces authors to write the field. Switching this\n * flag to `.default(false)` would now be safe on the authoring side, but it\n * would still narrow the output type and remove the tri-state above, so it is\n * deliberately left as-is.\n */\n anchorEntriesToScopeForeignKey: z.boolean().optional(),\n\n /** Optional rename ledger to preserve identity across business-key renames. */\n renames: z.array(DataSeedRenameSchema).optional(),\n\n /** Optional cross-seed dependency declarations (DAG resolved by Phase 2). */\n dependsOn: z.array(DataSeedRefSchema).optional(),\n\n /**\n * Required when `mode === SYNC` (D3 — destructive semantics must be\n * declared, never defaulted).\n */\n onOrphan: z.enum(DataSeedSyncOrphanPolicy).optional(),\n\n /**\n * How the reconcile loop decides \"already applied\" at version equality (PR-30, partial-resource\n * paper). Absent = VERSION (the historical contract, byte-identical). External-data pipeline seeds declare\n * CONTENT: their rows change remotely with no version bump, so change detection compares the\n * declared values against the persisted row at the managed paths.\n */\n reconcileStrategy: z.enum(DataSeedReconcileStrategy).optional(),\n\n /**\n * `ADDITIVE` only: what a pass does to a row that already exists. Absent =\n * {@link DataSeedExistingRowTreatment.REAPPLY_DECLARED_PAYLOAD}, the historical contract.\n * FORBIDDEN on every other mode (factory-enforced) — UPGRADE and SYNC already scope their writes\n * through `managedFields`, and LAZY never touches an existing row.\n */\n existingRowTreatment: z.enum(DataSeedExistingRowTreatment).optional(),\n\n /**\n * Required when (and only when) `mode === DataSeedMode.LAZY`. Carries\n * the lazy-access binding `(onResource, onOperation)` from which the\n * factory auto-injects a matching `LAZY_ACCESS` trigger. Authors of\n * non-LAZY seeds MUST omit this — declaring it on a non-LAZY seed is\n * rejected by the factory.\n */\n lazyAccess: DataSeedLazyAccessConfigSchema.optional(),\n\n /**\n * Optional cron-driven re-run binding (Phase 7, Item 47). Presence\n * of this block is the SOLE opt-in signal the\n * `data-seeding:scheduled-rerun` engine batch consults — seeds\n * without `schedule?` are NEVER picked up by the cron path.\n *\n * Mode incompatibilities (factory-enforced):\n *\n * - `mode === LAZY` + `schedule` is rejected — periodic re-runs\n * contradict LAZY's \"first access creates the row, never\n * re-applied\" contract.\n *\n * Cross-field invariants (factory-enforced):\n *\n * - `schedule.iterationStrategy` must be legal for `scope.kind`\n * per {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}.\n * - `schedule.cron` must parse via `cron-parser`.\n * - When `scopeIteration?` is ALSO declared, the two strategies\n * MUST agree — see {@link validateScopeIterationVsScheduleConsistency}.\n */\n schedule: DataSeedScheduleSchema.optional(),\n\n /**\n * Optional top-level scope-iteration declaration (Phase 7, Item 51 —\n * Slice 1, future-readiness).\n *\n * **Status — schema surface only**: this block is parsed and\n * validated at factory time, but the runtime Level-A non-cron path\n * still throws `NotImplemented` for ORG/USER/CUSTOM-scoped seeds\n * (`assertSeedIsSupportedForLevelA`). The schema lives in place so\n * that when Item 51's engine wiring lands (deferred until a real\n * consumer surfaces — see\n * `.cursor/plans/seeding-phase-7-multitenant-upgrade-sync.md`) the\n * declaration site is already canonical and Item 47's cron batch\n * can switch its read site from {@link DataSeedSchedule.iterationStrategy}\n * to {@link DataSeedScopeIteration.strategy} in a single targeted\n * edit, with no risk of silent divergence thanks to the\n * factory-time drift guard\n * ({@link validateScopeIterationVsScheduleConsistency}).\n *\n * Cross-field invariants (factory-enforced — see\n * {@link validateScopeIterationAgainstScope}):\n *\n * - FORBIDDEN when `scope.kind ∈ {GLOBAL, APPLICATION}` (single-anchor\n * scopes — there is nothing to iterate).\n * - `scopeIteration.strategy` must be legal for `scope.kind`\n * per {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}.\n * - When `schedule?` is ALSO declared, strategy + customResolverKey\n * on both blocks MUST agree (drift guard).\n *\n * NOT-required-for-now: ORG/USER/CUSTOM Level-A seeds may parse\n * successfully WITHOUT this block — the runtime gate from\n * `assertSeedIsSupportedForLevelA` remains the single source of\n * \"not supported\" truth at the engine layer until Item 51 Slice 2\n * lifts it. Forcing required-ness today would mislead authors into\n * adding a field they cannot use at runtime.\n */\n scopeIteration: DataSeedScopeIterationSchema.optional(),\n}).strict();\n\n/**\n * Inferred runtime (**output**) shape of {@link DataSeedDefinitionSchema},\n * without the non-Zod-modelable `entry` function and `migrators` map.\n * {@link DataSeedDefinition} extends this with those captured fields.\n *\n * Every field carrying a Zod `.default(...)` is REQUIRED here — by the time a\n * definition has this type the parser has already materialized the default. Do\n * not use this type to describe what an author writes; see\n * {@link DataSeedDefinitionAuthored}.\n */\nexport type DataSeedDefinitionParsed = z.infer<typeof DataSeedDefinitionSchema>;\n\n/**\n * Authoring (**input**) shape of {@link DataSeedDefinitionSchema} — what a\n * developer literally types at a declaration site, before parsing.\n *\n * The distinction from {@link DataSeedDefinitionParsed} is not academic. A field\n * declared with `.default(...)` is OPTIONAL on the way in and GUARANTEED on the\n * way out, so the two types genuinely differ and each one is correct for exactly\n * one audience. Deriving the authoring surface from the output type (as this\n * contract originally did) silently forces authors to hand-write every defaulted\n * field — which is how `managedFields: []` became mandatory boilerplate on every\n * `ADDITIVE` and `LAZY` seed, for a value the writer never reads on those modes.\n */\nexport type DataSeedDefinitionAuthored = z.input<typeof DataSeedDefinitionSchema>;\n\n/** @wildo_source:part:start saas.data-seeding.definition-input facet:kind:framework-api facet:audience:app-developer */\n/**\n * Authoring-time input shape passed to {@link createDataSeedDefinition}.\n * Adds the function-typed fields that Zod cannot validate — they are\n * passed through verbatim by the factory.\n *\n * `triggers` is optional only for LAZY seeds: the factory derives one trigger\n * from `lazyAccess`. Non-LAZY seeds must declare their trigger explicitly so\n * startup never guesses when a mutation is admissible.\n *\n * Required-vs-optional summary (cross-mode):\n *\n * - `seedKey`, `scope`, `resourceType`, `mode`, `version` — always required.\n * - `entries` xor `entry` — exactly one (static array vs computed\n * function). Computed `entry` requires at least one Level-B trigger.\n * - `managedFields` — OPTIONAL to write; defaults to `[]`. Required\n * non-empty for `UPGRADE` (the factory rejects an empty list, whether\n * you wrote it or inherited the default), recommended for `SYNC`.\n * OMIT it for `ADDITIVE` and `LAZY`: those modes never consult the\n * list — `ADDITIVE` re-stamps the FULL declared payload on every pass,\n * bypassing the projection entirely — so writing `managedFields: []`\n * there is noise that implies an ownership boundary the dispatcher\n * does not enforce.\n * - `triggers` — required for non-LAZY modes (≥1 trigger), MUST be\n * omitted for LAZY (factory auto-injects from `lazyAccess`).\n * - `lazyAccess` — required for LAZY, FORBIDDEN otherwise.\n * - `onOrphan` — required for SYNC, forbidden otherwise.\n * - `existingRowTreatment` — ADDITIVE only. Declare `FILL_MISSING_DEFAULTS`\n * when the seed CREATES a row a person owns afterwards (a config\n * singleton): the default re-applies the declared payload on every pass,\n * which replaces whole stored blocks.\n * - `renames` — optional rename ledger; each `rename.field` MUST be a\n * business-key field and `rename.to` MUST appear as a declared\n * business-key value (F-3 + F-10).\n * - `migrators` — optional per-version migrator map for UPGRADE; the\n * dispatcher walks `fromVersion` from the persisted row's\n * `seedVersion` up to `version - 1`, then re-applies\n * `managedFields` from the declared entries.\n * - `dependsOn` — optional cross-seed dependency declarations (DAG\n * resolved at dispatch time).\n * - `schedule` — optional cron-driven re-run binding; presence is the\n * sole opt-in signal for the engine cron batch. FORBIDDEN for\n * `mode === LAZY`.\n */\nexport interface DataSeedDefinitionInput extends DataSeedDefinitionAuthored, DataSeedDefinitionCallbacks {}\n\n/**\n * The two function-typed fields Zod cannot model or validate. The factory\n * captures them before the structural parse and re-attaches them to its result\n * verbatim, so they appear identically on the authoring and runtime shapes.\n */\nexport interface DataSeedDefinitionCallbacks {\n /**\n * Computed entry generator (mutually exclusive with `entries`). Typical\n * for Level-B seeds that need data from the triggering row\n * (`afterCreate` of an Organization → spawn its default Workspace).\n */\n entry?: DataSeedComputedEntryFn;\n /**\n * Per-version migrators applied sequentially from the persisted row's\n * recorded `seedVersion` up to `version - 1`. After all applicable\n * migrators run the dispatcher then re-applies the declared\n * `managedFields` from the seed entries.\n */\n migrators?: {\n fromVersion: Record<number, DataSeedMigratorFn>;\n };\n}\n\n/**\n * Runtime shape returned by {@link createDataSeedDefinition} and consumed\n * by the registry / dispatcher.\n *\n * This is built on the PARSED (output) shape, not on\n * {@link DataSeedDefinitionInput}, and the difference is load-bearing: every\n * defaulted field is guaranteed present here because the factory returns the\n * parser's result. Dispatcher and registry consumers can therefore read\n * `seed.managedFields` as a plain `string[]` — no `?? []` guard — even when the\n * author omitted it.\n *\n * `triggers` is additionally narrowed to GUARANTEED non-empty: the LAZY branch\n * is auto-populated by the factory and every other branch enforces explicit\n * declaration, so consumers can call `seed.triggers.some(...)` without a\n * defensive guard.\n */\nexport interface DataSeedDefinition extends DataSeedDefinitionParsed, DataSeedDefinitionCallbacks {\n triggers: DataSeedTrigger[];\n}\n/** @wildo_source:part:end saas.data-seeding.definition-input */\n\n// ---------------------------------------------------------------------------\n// 7. Factory (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Author a {@link DataSeedDefinition}.\n *\n * Validation pipeline:\n * 1. **Structural parse** via {@link DataSeedDefinitionSchema}. Catches\n * missing fields, wrong types, malformed enums.\n * 2. **§3.6 trigger × mode matrix** via\n * {@link validateDataSeedTriggerModeCombination} — see\n * `data-seed-trigger-mode-matrix.shared.utils.ts`. Same rule consulted\n * by the spec layer (`saas-specifications/.../data-seed-specification.schemas.ts`)\n * so authoring and specification cannot drift.\n * 3. **Mode-specific structural rules** below — these encode constraints\n * that cannot be expressed by the structural schema alone (e.g.\n * \"`SYNC` requires `onOrphan`\", \"exactly one of `entries`/`entry`\").\n *\n * On success returns the input verbatim — including the function-typed\n * `entry` and `migrators` fields (which Zod cannot validate).\n *\n * On failure throws a single descriptive `Error` whose message names the\n * failing `seedKey`, the failing rule, and (when relevant) the §3.6 cell.\n */\nexport function createDataSeedDefinition(\n definition: DataSeedDefinitionInput,\n): DataSeedDefinition {\n // (1) Structural parse — strip the function-typed fields before parse,\n // re-attach after, because `z.object().strict()` would reject them.\n //\n // L1-2 (post-Phase-7 close-out): wrap structural failures with the\n // `[DataSeed:<seedKey>] …` prefix that every other validator in\n // this factory emits, so authors get a uniformly grep-able identity\n // across every error class. The original `ZodError` is kept on the\n // `cause` chain so tooling can still introspect the structured\n // issues. We extract `seedKey` defensively from the input bag (which\n // is `unknown`-shaped before the parse succeeds) so a malformed\n // input that omitted `seedKey` still produces a usable message.\n const { entry, migrators, ...structural } = definition;\n let parsed: DataSeedDefinitionParsed;\n try {\n parsed = DataSeedDefinitionSchema.parse(structural);\n } catch (parseError) {\n const probableSeedKey = typeof (definition as { seedKey?: unknown }).seedKey === 'string'\n ? (definition as { seedKey: string }).seedKey\n : '<unknown>';\n const message = describeCaughtError(parseError);\n const wrapped = new Error(\n `[DataSeed:${probableSeedKey}] structural validation failed (Zod parse): ${message}`,\n );\n (wrapped as Error & { cause?: unknown }).cause = parseError;\n throw wrapped;\n }\n\n // (2) Lazy-access auto-derivation (Phase 5 audit F-2/F-12/F-18).\n // For LAZY mode the `lazyAccess` block is the SINGLE declaration\n // site for the trigger; the factory materializes the matching\n // `LAZY_ACCESS` trigger here so the registry's by-trigger-kind\n // index keeps working without authors having to re-declare the\n // same data. For non-LAZY modes the explicit `triggers` array\n // remains the only source of truth.\n const triggers = resolveDeclaredTriggers(parsed);\n\n // (3) Trigger × mode matrix — single source of truth lives in the shared\n // utility so saas-models and saas-specifications cannot diverge.\n for (const trigger of triggers) {\n validateDataSeedTriggerModeCombination({\n seedKey: parsed.seedKey,\n mode: parsed.mode,\n trigger,\n });\n }\n\n // (4) Mode-specific structural rules.\n validateEntriesVsEntry(parsed.seedKey, parsed.entries, entry);\n validateModeSpecificFields({ ...parsed, triggers }, entry);\n // (5) Rename invariants (F-3 + F-10 — Phase-4 deep review). Static\n // `entries` are required for the cross-check; computed `entry`\n // defers the same checks to dispatcher runtime (registered\n // resource schema can be consulted there). Skipping when\n // `entries` is absent matches the same pragmatism applied to\n // `validateEntriesVsEntry` above.\n if (parsed.renames !== undefined && parsed.entries !== undefined) {\n validateRenameInvariants(parsed.seedKey, parsed.renames, parsed.entries);\n }\n // (6) Schedule cross-field invariants (Phase 7, Item 47). Both\n // checks are no-ops when `schedule?` is absent — cron-aware\n // declaration is opt-in.\n if (parsed.schedule !== undefined) {\n validateDataSeedScheduleCronExpression(parsed.seedKey, parsed.schedule.cron);\n validateDataSeedScheduleAgainstScope(parsed.seedKey, parsed.scope.kind, parsed.schedule);\n }\n // (7) ScopeIteration cross-field invariants (Phase 7, Item 51 —\n // Slice 1, future-readiness). Forbidden on GLOBAL/APPLICATION\n // (single-anchor scopes) and must align with the same\n // ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND matrix the cron\n // path consults; when both `scopeIteration` and `schedule` are\n // declared, the drift guard rejects mismatched strategies. The\n // runtime engine still throws NotImplemented for ORG/USER/CUSTOM\n // Level-A — see `assertSeedIsSupportedForLevelA` — so the\n // factory deliberately does NOT require this field, only\n // validates it when present.\n if (parsed.scopeIteration !== undefined) {\n validateScopeIterationAgainstScope(parsed.seedKey, parsed.scope.kind, parsed.scopeIteration);\n if (parsed.schedule !== undefined) {\n validateScopeIterationVsScheduleConsistency(\n parsed.seedKey,\n parsed.scopeIteration,\n parsed.schedule,\n );\n }\n }\n // (8) Migrators key invariants (L1-3, post-Phase-7 close-out). Each\n // `migrators.fromVersion` key MUST be a positive integer\n // STRICTLY less than `parsed.version` so the dispatcher's\n // \"walk fromVersion+1 .. version\" loop has a defined endpoint\n // and no migrator is unreachable. Without this guard a typo\n // like `fromVersion: { 5: ... }` on a `version: 3` seed would\n // silently never run.\n if (migrators !== undefined) {\n validateMigratorsFromVersionKeys(parsed.seedKey, parsed.version, migrators);\n }\n\n // (9) Scope-FK injection opt-in (`anchorEntriesToScopeForeignKey`). Only\n // meaningful for a per-row scope that owns an FK column; a GLOBAL seed\n // has no owning scope, so the flag is a contradiction — fail fast rather\n // than silently no-op at dispatch.\n if (parsed.anchorEntriesToScopeForeignKey && parsed.scope.kind === DataSeedScopeKind.GLOBAL) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`anchorEntriesToScopeForeignKey\\` is set but \\`scope.kind\\` is GLOBAL — GLOBAL seeds have no owning scope FK to inject. Remove the flag or choose a per-row scope (APPLICATION / ORGANIZATION / USER / CUSTOM).`,\n );\n }\n\n return { ...parsed, triggers, entry, migrators };\n}\n\n/**\n * Migrators-key validator (L1-3, post-Pass-2 close-out fix:\n * Cat-2 — allow `fromVersion[0]`).\n *\n * Reject any `fromVersion` key that is not a NON-NEGATIVE integer\n * STRICTLY LESS THAN the declared `version`. Two failure modes:\n *\n * - **Negative / non-integer** keys (`-1`, `'1.5'`, `'abc'`) are\n * typos — string-keyed objects can collect anything at runtime,\n * so the validator has to re-parse with `Number()` and gate on\n * `Number.isInteger() && >= 0`.\n * - **Keys `>= declaredVersion`** are unreachable migrators. The\n * dispatcher's `runDataSeedMigratorChain` walks\n * `for v = persistedVersion .. declaredVersion - 1`, so any\n * `fromVersion[v]` with `v >= declaredVersion` never fires.\n *\n * **`fromVersion[0]` is intentionally legal** (audit Cat-2,\n * post-Pass-2 close-out): when a row was authored before any\n * `__seed.seedVersion` stamping (e.g. data backfilled before the\n * seed was registered, or an early-stage prototype that started at\n * v0), the migrator runner reads `persistedVersion: 0` and looks\n * up `migrators.fromVersion[0]` to upgrade it to v1. The earlier\n * Pass-1 validator rejected `numericKey <= 0` and silently broke\n * this v0 → v1 path; Pass-2 close-out relaxes the gate to\n * `numericKey < 0`.\n */\nfunction validateMigratorsFromVersionKeys(\n seedKey: string,\n declaredVersion: number,\n migrators: NonNullable<DataSeedDefinitionInput['migrators']>,\n): void {\n for (const rawKey of Object.keys(migrators.fromVersion)) {\n const numericKey = Number(rawKey);\n if (!Number.isInteger(numericKey) || numericKey < 0) {\n throw new Error(\n `[DataSeed:${seedKey}] \\`migrators.fromVersion\\` key \"${rawKey}\" must be a non-negative integer (got \"${rawKey}\").`,\n );\n }\n if (numericKey >= declaredVersion) {\n throw new Error(\n `[DataSeed:${seedKey}] \\`migrators.fromVersion\\` key ${numericKey} is unreachable: it must be strictly less than the declared seed version (${declaredVersion}). The dispatcher walks \\`v = persistedVersion .. version - 1\\`; a key ≥ version would never fire.`,\n );\n }\n }\n}\n\n/**\n * Resolve the runtime `triggers[]` array, applying the LAZY auto-derivation\n * contract (Phase 5 audit F-2/F-12/F-18).\n *\n * Rules:\n * - `mode === LAZY` MUST have `lazyAccess` AND MUST NOT declare\n * `triggers` explicitly. The factory injects exactly one\n * `LAZY_ACCESS` trigger from `lazyAccess.{onResource,onOperation}`.\n * - `mode !== LAZY` MUST declare `triggers` (≥1) AND MUST NOT declare\n * `lazyAccess`. The trigger array is returned verbatim.\n *\n * The \"MUST NOT\" branches are the divergence-prevention surface: if both\n * `lazyAccess` and `triggers` are declared, the engine would have to\n * pick one, and the unused declaration would silently rot. We reject at\n * authoring time so the bug surfaces immediately.\n */\nfunction resolveDeclaredTriggers(\n parsed: DataSeedDefinitionParsed,\n): DataSeedTrigger[] {\n if (parsed.mode === DataSeedMode.LAZY) {\n if (parsed.lazyAccess === undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=LAZY requires a \\`lazyAccess\\` configuration block (the single declaration site for the lazy-access trigger).`,\n );\n }\n if (parsed.triggers !== undefined && parsed.triggers.length > 0) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=LAZY auto-derives the trigger from \\`lazyAccess\\` — do NOT also declare \\`triggers\\` (Phase 5 audit F-2/F-12/F-18: single declaration site).`,\n );\n }\n return [{\n triggerKind: DataSeedTriggerKind.LAZY_ACCESS,\n resourceType: parsed.lazyAccess.onResource,\n operation: parsed.lazyAccess.onOperation,\n }];\n }\n\n // Non-LAZY modes.\n if (parsed.lazyAccess !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`lazyAccess\\` is only valid when mode=LAZY (got mode=${parsed.mode}).`,\n );\n }\n if (parsed.triggers === undefined || parsed.triggers.length === 0) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=${parsed.mode} requires at least one trigger.`,\n );\n }\n return parsed.triggers;\n}\n\n/**\n * Exactly one of `entries` (static) or `entry` (computed) must be provided.\n * Zod cannot express \"XOR of two optional fields\" cleanly without an opaque\n * `.refine` — keeping it as an explicit validator keeps the error message\n * actionable.\n */\nfunction validateEntriesVsEntry(\n seedKey: string,\n entries: DataSeedStaticEntry[] | undefined,\n entry: DataSeedComputedEntryFn | undefined,\n): void {\n const hasEntries = entries !== undefined && entries.length > 0;\n const hasEntry = entry !== undefined;\n if (hasEntries && hasEntry) {\n throw new Error(\n `[DataSeed:${seedKey}] Provide exactly one of \\`entries\\` (static array) or \\`entry\\` (computed function), not both.`,\n );\n }\n if (!hasEntries && !hasEntry) {\n throw new Error(\n `[DataSeed:${seedKey}] One of \\`entries\\` (static array) or \\`entry\\` (computed function) is required.`,\n );\n }\n}\n\n/**\n * Mode-specific cross-field rules. Each branch guards exactly one §6 design\n * decision so the failure messages map 1:1 to the design doc.\n *\n * Operates on the **resolved** triggers (post-{@link resolveDeclaredTriggers})\n * so the LAZY auto-injected trigger is included in cross-field checks.\n */\nfunction validateModeSpecificFields(\n parsed: DataSeedDefinitionParsed & { triggers: DataSeedTrigger[] },\n entry: DataSeedComputedEntryFn | undefined,\n): void {\n // D3 — `SYNC` requires explicit `onOrphan`.\n if (parsed.mode === DataSeedMode.SYNC && parsed.onOrphan === undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=SYNC requires an explicit \\`onOrphan\\` policy (D3 — destructive semantics must be declared).`,\n );\n }\n // L1-1 (post-Phase-7 close-out) — symmetric guard: `onOrphan` is\n // FORBIDDEN outside SYNC. The `DataSeedDefinitionInput` JSDoc\n // (line \"`onOrphan` — required for SYNC (D3), FORBIDDEN otherwise\")\n // is the contract; before this check the factory only enforced the\n // \"required\" half, so an author who left a stray `onOrphan: 'delete'`\n // on a non-SYNC seed (e.g. after refactoring SYNC → UPGRADE) would\n // see the field SILENTLY IGNORED — exactly the \"stray field rotting\"\n // failure mode the trigger × mode matrix is designed to prevent.\n // Rejecting here surfaces the bug at authoring time so the author\n // either drops the field or flips the mode back to SYNC.\n if (parsed.mode !== DataSeedMode.SYNC && parsed.onOrphan !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`onOrphan\\` is only valid when mode=SYNC (got mode=${parsed.mode}); drop the field or change \\`mode\\` to SYNC.`,\n );\n }\n // `existingRowTreatment` answers a question only ADDITIVE asks — see the enum JSDoc. Refused\n // elsewhere for the same reason as a stray `onOrphan`: a field the writer never reads would sit on\n // the declaration looking like a guarantee.\n if (parsed.mode !== DataSeedMode.ADDITIVE && parsed.existingRowTreatment !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`existingRowTreatment\\` is only valid when mode=ADDITIVE (got mode=${parsed.mode}); drop the field or change \\`mode\\` to ADDITIVE.`,\n );\n }\n // `UPGRADE` without managed fields is a no-op — the dispatcher would\n // have nothing to write. Guard now to surface the bug at authoring time.\n if (parsed.mode === DataSeedMode.UPGRADE && parsed.managedFields.length === 0) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=UPGRADE requires a non-empty \\`managedFields\\` list (otherwise the upgrade has nothing to apply).`,\n );\n }\n // The Phase 5 audit F-2/F-12/F-18 contract — `mode === LAZY` ⇒\n // exactly one auto-injected `LAZY_ACCESS` trigger — is enforced by\n // {@link resolveDeclaredTriggers} above; no duplicate check needed\n // here.\n // Computed `entry` only makes sense with at least one Level-B trigger.\n if (entry !== undefined && parsed.triggers.every(isLevelADataSeedTrigger)) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] computed \\`entry\\` requires at least one Level-B trigger ('resourceLifecycle' or 'lazyAccess'); use static \\`entries\\` for Level-A only seeds.`,\n );\n }\n // Phase 7 / Item 47 — N10: LAZY + schedule is semantically\n // contradictory. LAZY's contract is \"first access creates the row,\n // never re-applied\" (P5-C=C1 idempotency cache). A periodic\n // re-run would either be a no-op (the LAZY ledger row already\n // exists) or require deleting prior COMPLETED rows on every\n // tick — both wrong. Authors who want periodic refresh of LAZY-\n // shaped data should declare a SYNC seed with a `schedule` block\n // instead.\n if (parsed.mode === DataSeedMode.LAZY && parsed.schedule !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=LAZY + \\`schedule\\` is not allowed (LAZY's contract is \"first access creates the row, never re-applied\"; periodic re-runs are semantically incompatible). Declare a SYNC seed with a \\`schedule\\` instead if you need periodic refresh.`,\n );\n }\n}\n\n/**\n * Cross-check `renames[]` against declared static `entries[]` to catch\n * authoring typos at definition time rather than at the next pass —\n * F-3 + F-10 (Phase-4 deep review).\n *\n * --- F-10: `rename.field` MUST be part of the business-key shape ---\n *\n * Renames are a business-key relocation tool: their entire point is\n * to move a row from one business-key value to another so that the\n * dispatcher's `findByBusinessKey(...)` lookup re-locates the existing\n * row on the next pass. If `rename.field` isn't a business-key field,\n * the rename rewrites a non-identifying column instead — silently a\n * no-op for identity, and the next pass will INSERT a duplicate at\n * the new declared key. We detect this here by requiring that every\n * static entry's `businessKey` shape includes `rename.field`.\n *\n * Skipped when `entries` is absent (computed `entry` — runtime check).\n *\n * --- F-3: `rename.to` MUST be a declared value for `rename.field` ---\n *\n * The rename pipeline rewrites `field=from → field=to`, then the\n * reconcile loop expects to find rows at every declared business key.\n * If `to` doesn't appear as the value of `rename.field` on any declared\n * entry, the rename's only effect is to corrupt the row's business\n * key into a value the seed no longer authors — and on the next pass\n * the same row becomes an orphan (SYNC) or an unowned legacy row\n * (UPGRADE/ADDITIVE). Almost always a typo (`'new-namee'` vs\n * `'new-name'`); cheap to catch at authoring time.\n */\nfunction validateRenameInvariants(\n seedKey: string,\n renames: readonly DataSeedRename[],\n entries: readonly DataSeedStaticEntry[],\n): void {\n for (const rename of renames) {\n // F-10 — every entry's businessKey must include `rename.field`.\n for (const entry of entries) {\n if (!(rename.field in entry.businessKey)) {\n throw new Error(\n `[DataSeed:${seedKey}] rename.field '${rename.field}' is not part of the business key declared on entries[*].businessKey (F-10) — renames must target a business-key field, otherwise the rename silently no-ops and the next pass would insert a duplicate.`,\n );\n }\n }\n // F-3 — `rename.to` must appear as a declared value for that field.\n const declaredValuesForField = new Set<unknown>();\n for (const entry of entries) {\n const v = entry.businessKey[rename.field];\n if (v !== undefined) declaredValuesForField.add(v);\n }\n if (!declaredValuesForField.has(rename.to)) {\n throw new Error(\n `[DataSeed:${seedKey}] rename.to='${rename.to}' for field '${rename.field}' is not present on any declared entry's businessKey (F-3) — likely a typo. Declared values: [${[...declaredValuesForField].map(String).join(', ')}].`,\n );\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"data-seed.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/data-seeding/models/data-seed.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { CronExpressionParser } from 'cron-parser';\nimport { RuntimeEnvironment } from '@wildo-ai/saas-models';\nimport { z } from 'zod';\n\nimport {\n validateDataSeedTriggerModeCombination,\n} from './data-seed-trigger-mode-matrix.shared.utils';\nimport { describeCaughtError } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\n\n/**\n * ============================================================================\n * Data Seeding — declarative model (`saas-backend-lib/data-seeding/models`)\n * ============================================================================\n *\n * Source-of-truth for the runtime authoring shape of a `DataSeedDefinition`.\n * Application code imports these declarations from\n * `@wildo-ai/saas-backend-lib/data-seeding-models`; the specification layer\n * may add semantic guidance but uses the same runtime contract.\n *\n * Data seeding is backend-only: author declarations in the application backend\n * module and keep browser code free of seed execution concerns. The dedicated\n * public subpath avoids importing the full backend runtime merely to describe\n * a seed.\n *\n * Every public symbol uses the `DataSeed*` prefix so application declarations\n * remain unambiguous beside other bootstrap or agent configuration.\n */\n\n// ---------------------------------------------------------------------------\n// 1. Vocabulary — enums (Task 1.3.1)\n// ---------------------------------------------------------------------------\n\n/** @wildo_source:part:start saas.data-seeding.modes facet:kind:framework-api facet:audience:app-developer */\n/**\n * The four data-seeding modes — the **mode picker** is the single most\n * consequential authoring decision for a `DataSeedDefinition`. Each mode\n * has different write semantics and re-run safety. Pick by answering\n * \"what should happen on the SECOND pass?\":\n *\n * - **`LAZY`** — \"create on first access, never touch again\". Deferred\n * materialization, per-scope idempotency cache. Cannot combine with\n * `schedule?` (periodic re-run contradicts the contract). Write path\n * bypasses the operation pipeline (no hook re-entry from the seed's\n * own write).\n * - **`ADDITIVE`** — \"insert if business key absent; on present rows,\n * re-apply the entry's `data` payload via `upsertByBusinessKey`'s\n * `$set` semantics (no managed-fields gating, no version comparison)\".\n * Operationally additive — the dispatcher never deletes rows or\n * walks orphans for ADDITIVE — but each pass DOES re-stamp the\n * declared payload on every existing row. Use `UPGRADE` instead when\n * you need explicit version-gated mutation with `managedFields`\n * protecting user customization on non-managed columns. Do not describe\n * ADDITIVE as immutable for existing rows: its `upsertByBusinessKey`\n * behavior intentionally reapplies the declared payload.\n * - **`UPGRADE`** — \"insert missing; on present rows whose\n * `seedVersion < declared`, migrate via declared `migrators` and\n * re-apply `managedFields`\". Requires non-empty `managedFields`\n * (otherwise the upgrade has nothing to write). Preserves user\n * customization on non-managed fields.\n * - **`SYNC`** — \"declared set is source of truth: insert missing,\n * update changed managed fields, handle orphans per `onOrphan`\".\n * Requires explicit `onOrphan` policy (D3 — destructive semantics\n * must be declared, never defaulted). The most aggressive mode;\n * reserve for closed enums materialized as rows.\n *\n * Rejected alternatives:\n * - `REPLACE` (full overwrite of every field on every run) — hostile\n * to user customization. `SYNC` with managed-fields semantics covers\n * the same intent safely.\n */\nexport enum DataSeedMode {\n /** Create on first read/access if missing (per scope). Always re-run safe. */\n LAZY = 'lazy',\n /**\n * Insert if business key absent; on present rows, re-apply the\n * declared `entry.data` payload via `upsertByBusinessKey` (`$set`\n * semantics, no managed-fields gating, no version comparison).\n * Operationally additive at the row-set level (no delete, no orphan\n * walk) but NOT immutable per row — see schema-level mode JSDoc\n * (audit L2-6) for the full contract.\n */\n ADDITIVE = 'additive',\n /**\n * Insert if absent; if present and `seedVersion < declared` → migrate via\n * declared migrators (or replace declared-managed fields).\n */\n UPGRADE = 'upgrade',\n /**\n * Full reconciliation: declared set becomes source of truth. Insert missing,\n * update changed, optionally delete orphans (controlled by `onOrphan`).\n */\n SYNC = 'sync',\n}\n/** @wildo_source:part:end saas.data-seeding.modes */\n\n/**\n * Smallest unit at which a seed materializes (plan §3.5).\n *\n * The matching criterion is **always relative to the scope** — e.g. a seed\n * scoped at `ORGANIZATION` with business key `templateKey` matches on\n * `(organizationId, templateKey)`. The dispatcher (Phase 2) composes the\n * compound match from scope + business key automatically, eliminating the\n * \"did I forget the scope filter?\" class of bugs.\n */\nexport enum DataSeedScopeKind {\n /** Once per database. Rare. Trigger: onAppInit. */\n GLOBAL = 'global',\n /** Each APPLICATION row. Trigger: onAppInit. */\n APPLICATION = 'application',\n /** Each ORGANIZATION row. Trigger: organization-resource lifecycle. */\n ORGANIZATION = 'organization',\n /** Each USER row. Trigger: user-resource lifecycle. */\n USER = 'user',\n /** Each row of an arbitrary resource type, named in `relatedResourceType`. */\n CUSTOM = 'custom',\n}\n\n/**\n * Persisted classification on `DataSeedRun.triggerOrigin` (Phase 2 resource).\n *\n * Identifier-style spelling chosen for authoring ergonomics — matches the\n * trigger discriminator strings ('onAppInit', etc.) used in\n * {@link DataSeedTriggerSchema} below. Phase 2 may persist these directly or\n * map them to a kebab-cased external form; that's a persistence concern, not\n * an authoring concern.\n */\nexport enum DataSeedTriggerOrigin {\n APP_INIT = 'onAppInit',\n APP_VERSION_UPGRADE = 'onAppVersionUpgrade',\n RESOURCE_LIFECYCLE = 'resourceLifecycle',\n LAZY_ACCESS = 'lazyAccess',\n /**\n * Reserved placeholder for any non-`schedule.cron`-driven cron\n * primitive (e.g., a future low-level k8s `CronJob` that hits an\n * internal endpoint without going through `DataSeedDefinition.schedule`).\n * As of Phase 7 / Item 47 there is NO production consumer; the\n * declared-`schedule` cron path uses {@link SCHEDULED_RERUN} instead so\n * the audit ledger stays unambiguous about WHERE the cron came from.\n */\n CRON = 'cron',\n ADMIN_MANUAL = 'adminManual',\n /**\n * Phase 7 / Item 47 — fired when the engine-owned cron batch\n * (`data-seeding:scheduled-rerun`) re-dispatches a seed because\n * its declared `DataSeedDefinition.schedule.cron` matched the\n * current tick. Distinct from {@link CRON} (placeholder for\n * non-schedule-driven cron primitives) so dashboards / queries\n * can crisply distinguish the two cron pathways.\n *\n * Consumed by {@link ManualSeedEngineBackendService.runManual}'s\n * optional `originOverride` 3rd parameter (Slice 2). When passed,\n * the per-seed loop policy flips from \"abort on first failure\" to\n * \"continue, capture per-iteration failure\" (N6 / Q5=E2) — admin\n * reruns keep the strict abort semantics because a human is\n * watching, cron reruns must summarize failures rather than stop\n * the world.\n */\n SCHEDULED_RERUN = 'scheduled-rerun',\n}\n\n/**\n * Orphan handling for `DataSeedMode.SYNC` (D3 — explicit per declaration).\n *\n * `SYNC` reconciliation may discover rows in the DB whose business key is no\n * longer in the declared set. There is **no default** policy: the developer\n * must declare what to do, because each option has different operational\n * blast-radius and we refuse to encode the \"least surprising\" behavior on\n * the developer's behalf.\n */\n/**\n * How {@link reconcileDeclaredEntry} decides \"is this entry already applied\" at version equality\n * (PR-30, partial-resource paper). A CLOSED vocabulary with one named source — the external-data run\n * service maps `ExternalDataOrphanPolicy`-style declarations onto it at its seam.\n */\nexport enum DataSeedReconcileStrategy {\n /**\n * The historical contract: the seed VERSION is the change signal; version equality skips.\n * Right for authored seeds, whose entries only change when a developer edits them (and bumps).\n */\n VERSION = 'version',\n /**\n * The pipeline contract: rows change REMOTELY with no version bump, so the declared values are\n * compared against the persisted row AT THE MANAGED PATHS — a difference writes the overlay,\n * equality skips. Values are normalized before comparison (Date ⇄ ISO string), and a declared\n * `undefined` is NOT a difference (A1: the framework never clears from a missing source key).\n */\n CONTENT = 'content',\n}\n\n/**\n * What an `ADDITIVE` pass does to a row that ALREADY exists at the entry's business key.\n *\n * ## Why this is a declaration and not a property of the mode\n *\n * An entry payload is ambiguous in exactly one place, and it is the place that bit: `domains: {}`\n * can mean \"this block is empty\" or \"I declare nothing here — let the schema fill it\". Re-applying\n * the payload is right for the first reading (a seeded catalogue whose declared text must win) and\n * destructive for the second: the engine's own configuration singletons were seeded `ON_APP_INIT`\n * with `{}` blocks, and every boot replaced the verified domains, SSO enforcement and emergency\n * access an administrator had stored (#554). No function of the payload can tell the two apart, so\n * the author says which one they meant.\n *\n * ## Rejected alternatives\n *\n * - **Make ADDITIVE fill-only.** Silently changes every existing seed whose declared values are\n * meant to win on re-run (an application's own webhook-config seed re-stamps its dev payload on\n * purpose), and overlaying a vendor record onto a stored one would keep keys the vendor removed.\n * - **A fifth `DataSeedMode`.** The mode answers \"what happens to the SET of rows on the second\n * pass\"; this answers \"what happens to the FIELDS of one row\". Folding a field-level choice into\n * the row-set vocabulary would need a new trigger × mode matrix column for a distinction the matrix\n * has no opinion on.\n * - **Author the config seeds with no blocks at all.** Then an inserted row carries no block, which\n * is the internal-reader crash #554 was filed for.\n */\nexport enum DataSeedExistingRowTreatment {\n /**\n * The historical `ADDITIVE` contract and the default: the declared payload, materialised, is\n * `$set` onto the existing row on every pass. Right when the declaration is the source of truth\n * for the fields it names.\n */\n REAPPLY_DECLARED_PAYLOAD = 'reapply-declared-payload',\n /**\n * Insert when absent; on an existing row, assign ONLY the leaves the row is missing (see\n * `collectMissingSeedDefaultPaths`) and never replace a stored value. Right for a singleton that\n * the seed creates and a person owns afterwards — configuration rows above all.\n *\n * A seed declaring this on a `RESOURCE_LIFECYCLE` trigger also has its EXISTING rows repaired at\n * boot, once per seed version: such a seed never revisits the rows it created, so without that\n * pass a row written before a default existed would lack it forever.\n */\n FILL_MISSING_DEFAULTS = 'fill-missing-defaults',\n}\n\nexport enum DataSeedSyncOrphanPolicy {\n /** Hard delete the orphan row. Most aggressive. */\n DELETE = 'delete',\n /** Mark the row as deprecated (Phase 2 introduces the marker field). */\n DEPRECATE = 'deprecate',\n /** Do not touch the row; surface the diff in the run report. */\n REPORT = 'report',\n}\n\n// ---------------------------------------------------------------------------\n// 2. Scope reference (Task 1.3.1)\n// ---------------------------------------------------------------------------\n\n/**\n * Discriminated reference to \"where this seed materializes\". The CUSTOM\n * variant is the only one that requires `relatedResourceType` (any CoreResource\n * or app-resource type identifier). The other variants encode the resource\n * implicitly (APPLICATION/ORGANIZATION/USER are core resources, GLOBAL is\n * scope-less).\n */\nexport const DataSeedScopeRefSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal(DataSeedScopeKind.GLOBAL) }).strict(),\n z.object({ kind: z.literal(DataSeedScopeKind.APPLICATION) }).strict(),\n z.object({ kind: z.literal(DataSeedScopeKind.ORGANIZATION) }).strict(),\n z.object({ kind: z.literal(DataSeedScopeKind.USER) }).strict(),\n z\n .object({\n kind: z.literal(DataSeedScopeKind.CUSTOM),\n /**\n * The resource type whose rows define an instance of this scope. The\n * dispatcher (Phase 2) materializes the seed for every existing row of\n * this type (and again on each new row via the resource lifecycle hook).\n */\n relatedResourceType: z.string(),\n })\n .strict(),\n]);\n\nexport type DataSeedScopeRef = z.infer<typeof DataSeedScopeRefSchema>;\n\n/**\n * Cross-seed reference. Phase 1 only stores the symbolic name; Phase 2 adds\n * a `SeedReferenceResolver` that maps `seed.ref(seedId)` → matched row `_id`\n * at dispatch time, so renaming a business key never breaks foreign keys\n * (plan §3.3, §4.6).\n */\nexport const DataSeedRefSchema = z.object({\n seedKey: z.string(),\n});\n\nexport type DataSeedRef = z.infer<typeof DataSeedRefSchema>;\n\n// ---------------------------------------------------------------------------\n// 3. Trigger schema (Task 1.3.2)\n// ---------------------------------------------------------------------------\n\n/**\n * Resource lifecycle phase that fires a Level-B seed (plan §3.1).\n */\nexport enum DataSeedResourceLifecyclePhase {\n BEFORE_CREATE = 'beforeCreate',\n AFTER_CREATE = 'afterCreate',\n BEFORE_UPDATE = 'beforeUpdate',\n AFTER_UPDATE = 'afterUpdate',\n BEFORE_DELETE = 'beforeDelete',\n AFTER_DELETE = 'afterDelete',\n}\n\n/**\n * Discriminator constants used by {@link DataSeedTriggerSchema}. Exported so\n * downstream code (matrix validator, dispatcher) can use the same literals\n * as the schema.\n */\nexport enum DataSeedTriggerKind {\n ON_APP_INIT = 'onAppInit',\n ON_APP_VERSION_UPGRADE = 'onAppVersionUpgrade',\n ON_SCHEDULED_REFRESH = 'onScheduledRefresh',\n ON_MANUAL_TRIGGER = 'onManualTrigger',\n RESOURCE_LIFECYCLE = 'resourceLifecycle',\n LAZY_ACCESS = 'lazyAccess',\n}\n\n/**\n * Trigger discriminated union (plan §3.1).\n *\n * The four payload-less variants are **Level A** (application lifecycle).\n * `resourceLifecycle` and `lazyAccess` are **Level B** (per-row). The\n * segregation is enforced by {@link validateDataSeedTriggerModeCombination}\n * at factory time — see plan §3.6 for the allowed cells.\n */\nexport const DataSeedTriggerSchema = z.discriminatedUnion('triggerKind', [\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_APP_INIT) }).strict(),\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_APP_VERSION_UPGRADE) }).strict(),\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_SCHEDULED_REFRESH) }).strict(),\n z.object({ triggerKind: z.literal(DataSeedTriggerKind.ON_MANUAL_TRIGGER) }).strict(),\n z.object({\n triggerKind: z.literal(DataSeedTriggerKind.RESOURCE_LIFECYCLE),\n /** The resource type whose lifecycle event fires this seed. */\n resourceType: z.string(),\n /** Phase of the resource operation that fires the seed. */\n phase: z.enum(DataSeedResourceLifecyclePhase),\n })\n .strict(),\n z.object({\n triggerKind: z.literal(DataSeedTriggerKind.LAZY_ACCESS),\n /**\n * The resource read by the user that triggers materialization.\n *\n * Phase 5 audit F-2/F-12/F-18: this field is **auto-injected by the\n * factory** from `seed.lazyAccess.onResource`. Authors MUST NOT\n * declare a `LAZY_ACCESS` trigger explicitly — declare the\n * `lazyAccess` config block instead and the factory will materialize\n * a single matching trigger here. Single source of truth.\n */\n resourceType: z.string(),\n /**\n * The operation (`LIST`, `READ`, …) whose first call triggers the\n * seed. Auto-injected from `seed.lazyAccess.onOperation`.\n */\n operation: z.string(),\n // No `matchScope` — `seed.scope.kind` is the single source of truth\n // for the scope at which \"first access\" is evaluated. The previous\n // duplicate field on this trigger and on `lazyAccess` could silently\n // diverge from `seed.scope.kind`; the dispatcher always consumed\n // `seed.scope.kind`, so the duplicates were dead-write surface\n // for divergence bugs.\n })\n .strict(),\n]);\n\nexport type DataSeedTrigger = z.infer<typeof DataSeedTriggerSchema>;\n\n/**\n * True iff the trigger is one of the Level-A application lifecycle variants.\n * Re-exported here so the factory and the matrix validator share one\n * classification function.\n */\nexport function isLevelADataSeedTrigger(trigger: DataSeedTrigger): boolean {\n return (\n trigger.triggerKind === DataSeedTriggerKind.ON_APP_INIT\n || trigger.triggerKind === DataSeedTriggerKind.ON_APP_VERSION_UPGRADE\n || trigger.triggerKind === DataSeedTriggerKind.ON_SCHEDULED_REFRESH\n || trigger.triggerKind === DataSeedTriggerKind.ON_MANUAL_TRIGGER\n );\n}\n\n/**\n * True iff the trigger is one of the Level-B per-row variants\n * (`resourceLifecycle` or `lazyAccess`). Symmetric with\n * {@link isLevelADataSeedTrigger}; provided so call sites read as\n * `isLevelBDataSeedTrigger(t)` rather than `!isLevelADataSeedTrigger(t)`.\n *\n * Implemented by negating the Level-A check rather than re-listing the\n * Level-B kinds: keeping the two helpers tied means adding a new Level-A\n * trigger automatically extends both predicates without a coverage hole.\n */\nexport function isLevelBDataSeedTrigger(trigger: DataSeedTrigger): boolean {\n return !isLevelADataSeedTrigger(trigger);\n}\n\n// ---------------------------------------------------------------------------\n// 4. Entries (static + computed) (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * A single static seed row authored at declaration time.\n *\n * `businessKey` is an arbitrary record matching either a single\n * `isBusinessKey()`-marked field or a composite `businessKeySet([...])` on\n * the target resource. The dispatcher (Phase 2) validates shape against the\n * resource's schema; Phase 1 keeps it permissive.\n *\n * `data` is the row payload (excluding `_id`, which is generated by the\n * dispatcher when the row is inserted, and `__seed` which is system-managed).\n *\n * **Deferred validation — L1-4 (post-Phase-7 close-out):** the\n * factory does NOT cross-check that the keys present on\n * `businessKey` match the `isBusinessKey()` / `businessKeySet([...])`\n * declarations on the target resource's Zod schema. The check\n * REQUIRES read access to the resource registry, which is\n * intentionally not wired into the seed factory (`createDataSeedDefinition`\n * runs at module-init time, BEFORE the resource registry is fully\n * populated — wiring it would force a load-order coupling and risk\n * a circular import between `data-seeding/models` and\n * `resources/services`). The dispatcher performs the equivalent\n * runtime check on the first pass: a mismatched `businessKey`\n * produces a \"businessKey shape does not match resource declaration\"\n * failure surfaced through the standard `DataSeedRun.failureReport`\n * channel. Wiring a registry-lookup at factory time is left as a\n * future enhancement (would need a deferred-validation queue\n * flushed after the registry's bootstrap step — see plan §3.4\n * bootstrap chain), but the runtime path is sufficient because\n * authors run the dispatcher in CI on every change.\n */\nexport const DataSeedStaticEntrySchema = z.object({\n businessKey: z.record(z.string(), z.unknown()),\n data: z.record(z.string(), z.unknown()),\n}).strict();\n\nexport type DataSeedStaticEntry = z.infer<typeof DataSeedStaticEntrySchema>;\n\n/**\n * Function shape of a **computed** seed entry — used by Level B seeds whose\n * payload depends on the triggering row (e.g. an `afterCreate` seed needs\n * the `_id` of the row that triggered it). Captured outside the Zod schema\n * because Zod cannot validate function return types at parse time.\n *\n * `triggeringRow` is opaque (`unknown`) at this layer — Phase 2's typed\n * dispatcher narrows it to the resource's row type at the call site.\n */\nexport type DataSeedComputedEntryFn = (context: {\n /**\n * The resource row whose lifecycle event fired the seed (Level B only).\n * Always present for `triggerKind: 'resourceLifecycle'`. `null` for lazy\n * access seeds where there is no triggering row.\n */\n triggeringRow: { _id: string } & Record<string, unknown> | null;\n /**\n * Read-only execution context: tunneled audit attribution only. Seeds\n * always run as `INTERNALLY_INITIATED_REQUEST` regardless (D4) — this\n * field is for capturing `triggeredByUserId` into the row's `__seed`\n * ledger and the `DataSeedRun` record.\n */\n executionContext: {\n triggeredByUserId: string | null;\n };\n}) => DataSeedStaticEntry | Promise<DataSeedStaticEntry>;\n\n// ---------------------------------------------------------------------------\n// 5. Migrators + renames (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Migrator function for `DataSeedMode.UPGRADE`. Receives the persisted row\n * (cast to `Record<string, unknown>` at this layer; Phase 2 narrows) and\n * returns the upgraded form. The dispatcher applies migrators sequentially\n * starting from the persisted version's migrator and walking up to the\n * declared version, before then re-applying the declared `managedFields`.\n */\nexport type DataSeedMigratorFn = (row: Record<string, unknown>) =>\n Record<string, unknown>\n | Promise<Record<string, unknown>>;\n\n/**\n * Business-key rename declaration. The dispatcher uses these to find a row\n * by its **previous** business-key value and update it to the new value\n * before reconciliation runs — preserving identity across renames.\n */\nexport const DataSeedRenameSchema = z.object({\n field: z.string(),\n from: z.string(),\n to: z.string(),\n}).strict();\n\nexport type DataSeedRename = z.infer<typeof DataSeedRenameSchema>;\n\n/**\n * Lazy-access binding — the **single declaration site** for a LAZY seed's\n * trigger. Required when (and only when) `mode === DataSeedMode.LAZY`.\n *\n * Phase 5 audit F-2/F-12/F-18 consolidated three former declaration sites\n * into one: previously `(resourceType, operation, matchScope)` could be\n * declared on `seed.lazyAccess`, on `seed.triggers[*]` (a `LAZY_ACCESS`\n * trigger), AND `matchScope` also overlapped with `seed.scope.kind`.\n * Three sites for the same data → silent-divergence bugs. Today:\n *\n * - `(onResource, onOperation)` live ONLY here. The factory\n * auto-injects a matching `LAZY_ACCESS` trigger into `seed.triggers`\n * so the registry's by-trigger-kind index keeps working without\n * authors having to re-declare the same data.\n * - The \"scope at which first access is evaluated\" is `seed.scope.kind`\n * (was previously also called `matchScope` here — the field was\n * removed because the dispatcher only ever consulted `seed.scope.kind`,\n * making the local copy a dead-write surface for divergence bugs).\n *\n * Phase 5 (LAZY engine) decisions baked into this contract:\n *\n * **P5-E (E2 — any operation allowed)**: `onOperation` is intentionally a\n * free-form string with NO factory-time allowlist. The canonical\n * use-cases are READ + LIST (\"default dashboard appears the first time\n * a user opens the dashboard list\"), but mutations (CREATE/UPDATE/DELETE)\n * are also legal — the lazy guard registration in\n * {@link wireLazyAccessGuards} simply hooks whichever operation the seed\n * declares. Self-recursion is safe because seed bodies write via the\n * REPOSITORY-layer `repo.upsertByBusinessKey` (bypasses the operation\n * pipeline → no hook re-entry from the seed's own write). Re-entrancy\n * hardening at {@link DataSeedDispatcherBackendService.runLazy}\n * additionally short-circuits when `parentCtx.executionType ===\n * INTERNALLY_INITIATED_REQUEST` or `parentCtx.subCallDepth > 0`.\n *\n * **P5-B (B2 — all 5 scopes supported)**: `seed.scope.kind` may be any\n * of GLOBAL / APPLICATION / USER / ORGANIZATION / CUSTOM. Resolution to\n * a concrete `relatedId` happens at dispatch time inside\n * `resolveLazyMatchScopeAnchor`:\n * - GLOBAL → `null`\n * - APPLICATION → `parentCtx.applicationId` (CONFIGURATION error if undefined)\n * - USER → `parentCtx.userId` (CONFIGURATION error if undefined — anonymous callers cannot trigger user-scoped lazy seeds)\n * - ORGANIZATION → `parentCtx.organizationId` (CONFIGURATION error if undefined)\n * - CUSTOM → first `parentCtx.relationshipContexts[*]` whose `resourceType === seed.scope.relatedResourceType` (CONFIGURATION error if absent)\n *\n * **P5-C (C1 — no version-gate re-run)**: LAZY is \"first access creates\n * the row, never re-applied\". The dispatcher checks for ANY existing\n * `DataSeedRun` row with `status: COMPLETED` for\n * `(seedKey, scopeKind, relatedResourceType, relatedId)` regardless of\n * `seed.version`. If you need re-apply-on-version-bump semantics,\n * declare an UPGRADE seed instead.\n */\nexport const DataSeedLazyAccessConfigSchema = z.object({\n /** The resource whose access triggers the seed. */\n onResource: z.string(),\n /**\n * The operation whose first call triggers the seed. Canonical:\n * READ / LIST. Mutations are technically allowed (P5-E=E2) but\n * discouraged for non-system seeds because the user-facing operation\n * may semantically conflict with the framework-initiated row write.\n */\n onOperation: z.string(),\n}).strict();\n\nexport type DataSeedLazyAccessConfig = z.infer<typeof DataSeedLazyAccessConfigSchema>;\n\n// ---------------------------------------------------------------------------\n// 5.5. Schedule + scope iteration (Phase 7 — Item 47, cron scheduler)\n// ---------------------------------------------------------------------------\n\n/** @wildo_source:part:start saas.data-seeding.scope-iteration-strategy facet:kind:framework-api facet:audience:app-developer */\n/**\n * The five iteration strategies a scheduled seed may declare. Together they\n * cover every {@link DataSeedScopeKind} without making scope enumeration an\n * implicit, unreviewed behavior.\n *\n * The first four are framework-resolved (the engine paginates the\n * canonical resource for that scope kind via the `iterateAllOfResource`\n * shared helper). `CUSTOM_RESOLVER` defers scope enumeration to a\n * developer-supplied function registered under\n * {@link DataSeedScheduleSchema.shape.customResolverKey} — required when\n * `scope.kind === CUSTOM` and available as an opt-in for the three\n * resource-aligned scopes (e.g., \"all applications matching this\n * filter\", \"users in this org bucket\").\n *\n * This is deliberately separate from {@link DataSeedScopeKind}: operator\n * intent and scope kind are orthogonal concerns. Auto-deriving the strategy\n * from `scope.kind` would foreclose the valid use case \"I\n * want a curated subset of applications, not all of them\" (which\n * `CUSTOM_RESOLVER` covers cleanly even on an `APPLICATION`-scoped\n * seed). Keeping them separate lets an application refine its target audience\n * without changing the seed's data ownership contract.\n */\nexport enum DataSeedScopeIterationStrategy {\n /** Single dispatch with `scope: { kind: GLOBAL }`. Only legal for `scope.kind === GLOBAL`. */\n GLOBAL = 'global',\n /** Paginate every row of the `applications` core resource, dispatch once per `_id`. */\n ALL_APPLICATIONS = 'all-applications',\n /** Paginate every row of the `organizations` core resource, dispatch once per `_id`. */\n ALL_ORGANIZATIONS = 'all-organizations',\n /** Paginate every row of the `users` core resource, dispatch once per `_id`. */\n ALL_USERS = 'all-users',\n /** Defer scope enumeration to a developer-registered resolver (see `customResolverKey`). */\n CUSTOM_RESOLVER = 'custom-resolver',\n}\n/** @wildo_source:part:end saas.data-seeding.scope-iteration-strategy */\n\n/** @wildo_source:part:start saas.data-seeding.schedule facet:kind:framework-api facet:audience:app-developer */\n/**\n * Schedule + scope-iteration block on a {@link DataSeedDefinition}.\n *\n * Presence of this object is the sole opt-in signal for scheduled re-runs.\n * Seeds without `schedule?` are never selected by the cron path; they run only\n * through their declared lifecycle, lazy-access or operator-triggered paths.\n *\n * **Field-level invariants enforced by the factory** (not by Zod alone):\n *\n * - `cron` — parsed by `cron-parser` at factory time\n * ({@link validateDataSeedScheduleCronExpression}); reject malformed\n * expressions early so app startup fails loudly instead of the next\n * tick silently logging a parse error.\n * - `iterationStrategy` — must satisfy the alignment matrix in\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}, enforced by\n * {@link validateDataSeedScheduleAgainstScope}.\n * - `customResolverKey` — REQUIRED iff\n * `iterationStrategy === CUSTOM_RESOLVER`; FORBIDDEN otherwise\n * (enforced inline by the schema's `superRefine`).\n * - **Mode** — `mode === LAZY` + `schedule` is rejected at factory\n * time (a LAZY seed's contract is \"first access creates the row,\n * never re-applied\" — periodic re-runs contradict that). Enforced by\n * {@link validateModeSpecificFields}.\n *\n * `customResolverKey` is a string lookup against the application-registered\n * resolver. The definition never carries a function directly: keeping it\n * declarative lets the registry and companion inspect the schedule safely.\n */\nexport const DataSeedScheduleSchema = z\n .object({\n /**\n * Standard 5-field crontab expression (e.g. `'0 * * * *'` for\n * hourly). Validated at factory time via `cron-parser`; a malformed\n * expression throws with the seed key in the message so app\n * startup surfaces it immediately.\n */\n cron: z.string().min(1),\n /**\n * Which scope tuples this seed re-runs over per cron tick. Must\n * align with the seed's declared `scope.kind` per\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}.\n */\n iterationStrategy: z.enum(DataSeedScopeIterationStrategy),\n /**\n * String key looked up against the runtime\n * `DataSeedScopeResolverRegistryBackendService`. Resolvers are\n * registered at app startup via\n * `BackendDomainModule.dataSeedScopeResolvers` (aggregated by\n * `mergeBackendDomainModules` and threaded through\n * `ApplicationInitializationConfig.dataSeedScopeResolvers`).\n * REQUIRED when `iterationStrategy === CUSTOM_RESOLVER`,\n * FORBIDDEN otherwise.\n */\n customResolverKey: z.string().min(1).optional(),\n })\n .strict()\n .superRefine((value, ctx) => {\n const requiresResolver\n = value.iterationStrategy === DataSeedScopeIterationStrategy.CUSTOM_RESOLVER;\n const hasResolver = value.customResolverKey !== undefined;\n if (requiresResolver && !hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is required when iterationStrategy === CUSTOM_RESOLVER (the resolver returns the explicit list of relatedIds to dispatch).',\n });\n }\n if (!requiresResolver && hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is only valid when iterationStrategy === CUSTOM_RESOLVER (other strategies are framework-resolved and would silently ignore the resolver).',\n });\n }\n });\n\nexport type DataSeedSchedule = z.infer<typeof DataSeedScheduleSchema>;\n/** @wildo_source:part:end saas.data-seeding.schedule */\n\n/**\n * Strategy ↔ scope-kind alignment matrix. Single source of truth shared\n * by:\n *\n * 1. {@link validateDataSeedScheduleAgainstScope} — factory-time\n * rejection of misaligned declarations (fail-fast at app startup).\n * 2. The future `iterateAllOfResource` shared helper (Slice 4) —\n * runtime sanity assert that the seed's resolved strategy still\n * matches its declared scope (defense-in-depth — a divergence\n * here would mean a registry mutation slipped past the factory).\n *\n * The matrix is intentionally exhaustive (every {@link DataSeedScopeKind}\n * key present): TypeScript exhaustiveness checking on the Record type\n * forces us to add an entry whenever a new scope kind ships. Empty\n * arrays would be a code smell — every scope kind must have AT LEAST\n * one legal iteration strategy or cron-aware seeds at that scope kind\n * are unreachable.\n */\nexport const ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND: Readonly<\n Record<DataSeedScopeKind, ReadonlyArray<DataSeedScopeIterationStrategy>>\n> = {\n [DataSeedScopeKind.GLOBAL]: [DataSeedScopeIterationStrategy.GLOBAL],\n [DataSeedScopeKind.APPLICATION]: [\n DataSeedScopeIterationStrategy.ALL_APPLICATIONS,\n DataSeedScopeIterationStrategy.CUSTOM_RESOLVER,\n ],\n [DataSeedScopeKind.ORGANIZATION]: [\n DataSeedScopeIterationStrategy.ALL_ORGANIZATIONS,\n DataSeedScopeIterationStrategy.CUSTOM_RESOLVER,\n ],\n [DataSeedScopeKind.USER]: [\n DataSeedScopeIterationStrategy.ALL_USERS,\n DataSeedScopeIterationStrategy.CUSTOM_RESOLVER,\n ],\n [DataSeedScopeKind.CUSTOM]: [DataSeedScopeIterationStrategy.CUSTOM_RESOLVER],\n};\n\n/**\n * Throw with an actionable error when `iterationStrategy` is not legal\n * for `scopeKind`. Called from {@link createDataSeedDefinition} after\n * the structural parse + scope/trigger checks have run.\n *\n * Why a separate function (not a `.superRefine` on\n * {@link DataSeedScheduleSchema}): the schedule schema is decoupled\n * from the scope-kind context — only the factory has both pieces in\n * scope. Keeping them separated avoids `superRefine` chains that\n * couple two otherwise-orthogonal authoring surfaces.\n */\nexport function validateDataSeedScheduleAgainstScope(\n seedKey: string,\n scopeKind: DataSeedScopeKind,\n schedule: DataSeedSchedule,\n): void {\n const allowed = ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND[scopeKind];\n if (!allowed.includes(schedule.iterationStrategy)) {\n throw new Error(\n `[DataSeed:${seedKey}] schedule.iterationStrategy='${schedule.iterationStrategy}' is not legal for scope.kind='${scopeKind}'. Allowed values: [${allowed.join(', ')}].`,\n );\n }\n}\n\n/**\n * Top-level scope-iteration declaration block (Phase 7 / Item 51 —\n * Slice 1, future-readiness).\n *\n * **Status as of 2026-04-17 — schema surface only**: this block is\n * accepted by the factory and validated against the seed's `scope.kind`\n * + the `ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND` matrix, but the\n * runtime Level-A non-cron path\n * ({@link DataSeedDispatcherBackendService.runApplicationLifecycle})\n * still throws `NotImplemented` for ORG/USER/CUSTOM-scoped seeds\n * (`assertSeedIsSupportedForLevelA`). Authors declaring this block\n * today will hit that runtime gate; the schema is in place so the day\n * the engine wiring lands (Item 51 Slices 2–5, deferred until a real\n * consumer surfaces — see\n * `.cursor/plans/seeding-phase-7-multitenant-upgrade-sync.md`) the\n * declaration site is already canonical and the cron batch's read\n * site can switch from {@link DataSeedSchedule.iterationStrategy} to\n * {@link DataSeedScopeIteration.strategy} in a single targeted edit.\n *\n * **Why a separate top-level block** (not a derived view of\n * `schedule`): the cron path (Item 47) and the non-cron Level-A\n * lifecycle path (Item 51) iterate the same fan-out shape but are\n * triggered by orthogonal mechanisms. Coupling iteration intent to\n * `schedule` would force every multi-tenant Level-A seed to declare\n * an unwanted cron just to express \"iterate per-row at startup\". A\n * top-level field carries the iteration intent independently of WHEN\n * the iteration runs.\n *\n * **Drift guard** (load-bearing during the dual-declaration window):\n * when both `seed.scopeIteration` AND `seed.schedule.iterationStrategy`\n * are present, the factory rejects mismatched declarations\n * ({@link validateScopeIterationVsScheduleConsistency}). This is what\n * lets Item 47's cron batch keep reading `schedule.iterationStrategy`\n * without risk of silent divergence; once Item 51's engine wiring\n * lands the cron read site can be switched in confidence.\n *\n * **Inline `superRefine`** (`customResolverKey` REQUIRED iff\n * `strategy === CUSTOM_RESOLVER`) mirrors {@link DataSeedScheduleSchema}\n * — same vocabulary, same gate, same error wording so authors who\n * have learned one block carry that knowledge to the other.\n */\nexport const DataSeedScopeIterationSchema = z\n .object({\n /**\n * Which scope tuples this seed iterates over per non-cron Level-A\n * dispatch. Must align with the seed's declared `scope.kind` per\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}; the factory\n * additionally rejects this whole block on GLOBAL/APPLICATION-scoped\n * seeds (single-anchor at runtime, no iteration possible — see\n * {@link validateScopeIterationAgainstScope}).\n */\n strategy: z.enum(DataSeedScopeIterationStrategy),\n /**\n * String key looked up against the runtime\n * `DataSeedScopeResolverRegistryBackendService` (Item 47 / Slice 3)\n * when `strategy === CUSTOM_RESOLVER`. REQUIRED in that case,\n * FORBIDDEN otherwise — same contract as\n * {@link DataSeedScheduleSchema.shape.customResolverKey}.\n */\n customResolverKey: z.string().min(1).optional(),\n })\n .strict()\n .superRefine((value, ctx) => {\n const requiresResolver\n = value.strategy === DataSeedScopeIterationStrategy.CUSTOM_RESOLVER;\n const hasResolver = value.customResolverKey !== undefined;\n if (requiresResolver && !hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is required when strategy === CUSTOM_RESOLVER (the resolver returns the explicit list of relatedIds to dispatch).',\n });\n }\n if (!requiresResolver && hasResolver) {\n ctx.addIssue({\n code: 'custom',\n path: ['customResolverKey'],\n message:\n 'customResolverKey is only valid when strategy === CUSTOM_RESOLVER (other strategies are framework-resolved and would silently ignore the resolver).',\n });\n }\n });\n\nexport type DataSeedScopeIteration = z.infer<typeof DataSeedScopeIterationSchema>;\n\n/**\n * Throw with an actionable error when `scopeIteration` is declared on\n * a single-anchor scope (GLOBAL/APPLICATION) — these scopes resolve to\n * exactly one anchor at runtime\n * (`resolveLevelAScopeAnchor`/`resolveLevelBScopeAnchor`), so iterating\n * is meaningless. Then enforce the strategy-vs-scope alignment matrix\n * for the remaining ORG/USER/CUSTOM scopes via the existing\n * {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND} matrix (single\n * source of truth shared with the cron path).\n *\n * Why a separate function (not a `.superRefine` on\n * {@link DataSeedScopeIterationSchema}): same reasoning as\n * {@link validateDataSeedScheduleAgainstScope} — the iteration schema\n * is decoupled from the scope-kind context; only the factory has both\n * pieces in scope. Keeping them separated avoids `superRefine` chains\n * that couple two otherwise-orthogonal authoring surfaces.\n */\nexport function validateScopeIterationAgainstScope(\n seedKey: string,\n scopeKind: DataSeedScopeKind,\n scopeIteration: DataSeedScopeIteration,\n): void {\n if (\n scopeKind === DataSeedScopeKind.GLOBAL\n || scopeKind === DataSeedScopeKind.APPLICATION\n ) {\n throw new Error(\n `[DataSeed:${seedKey}] scope.kind='${scopeKind}' resolves to a single anchor at runtime; declaring \\`scopeIteration\\` here is meaningless (there is nothing to iterate). Remove the \\`scopeIteration\\` block — it is only valid for ORGANIZATION/USER/CUSTOM-scoped seeds where multiple per-row anchors exist.`,\n );\n }\n const allowed = ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND[scopeKind];\n if (!allowed.includes(scopeIteration.strategy)) {\n throw new Error(\n `[DataSeed:${seedKey}] scopeIteration.strategy='${scopeIteration.strategy}' is not legal for scope.kind='${scopeKind}'. Allowed values: [${allowed.join(', ')}].`,\n );\n }\n}\n\n/**\n * Drift guard: when BOTH `scopeIteration` AND `schedule` are declared\n * on the same seed, their iteration intent MUST match — the cron path\n * (Item 47) reads from `schedule.iterationStrategy` today; the\n * non-cron Level-A path (Item 51, future) will read from\n * `scopeIteration.strategy`. If the two declarations disagreed, the\n * cron tick and the lifecycle dispatch would fan out the same seed\n * through different anchor sets — a silent divergence the framework\n * cannot recover from at runtime.\n *\n * This check is what allows Item 51 Slice 1 to ship a future-ready\n * schema surface WITHOUT touching Item 47's cron batch read site:\n * authors who declare both blocks must keep them in agreement, so\n * once Item 51 Slice 2 lands the cron batch can switch its read site\n * to the new field with no behavioral change.\n */\nexport function validateScopeIterationVsScheduleConsistency(\n seedKey: string,\n scopeIteration: DataSeedScopeIteration,\n schedule: DataSeedSchedule,\n): void {\n if (scopeIteration.strategy !== schedule.iterationStrategy) {\n throw new Error(\n `[DataSeed:${seedKey}] scopeIteration.strategy='${scopeIteration.strategy}' disagrees with schedule.iterationStrategy='${schedule.iterationStrategy}'. The two declaration sites describe the same scope-fan-out intent and MUST match — the cron path and the lifecycle path cannot iterate the same seed via different strategies.`,\n );\n }\n if (scopeIteration.customResolverKey !== schedule.customResolverKey) {\n throw new Error(\n `[DataSeed:${seedKey}] scopeIteration.customResolverKey='${scopeIteration.customResolverKey ?? 'undefined'}' disagrees with schedule.customResolverKey='${schedule.customResolverKey ?? 'undefined'}'. When both declarations are present they MUST resolve to the same registered resolver — the cron path and the lifecycle path cannot iterate the same seed via different resolvers.`,\n );\n }\n}\n\n/**\n * Validate `schedule.cron` parses cleanly at factory time. Wraps the\n * `cron-parser` throw so the error message names the failing seed —\n * otherwise the bare parser error names the malformed expression but\n * not which seed declared it, forcing operators to grep.\n *\n * `cron-parser` 5.x throws on invalid input; we re-throw with the\n * seedKey context. We do NOT cache the parsed expression here — the\n * cron batch (Slice 5) re-parses per tick because the parser carries\n * mutable iteration state (`next()`, `prev()`) that we don't want to\n * leak between ticks.\n */\nexport function validateDataSeedScheduleCronExpression(\n seedKey: string,\n cron: string,\n): void {\n try {\n CronExpressionParser.parse(cron);\n }\n catch (err) {\n const message = describeCaughtError(err);\n throw new Error(\n `[DataSeed:${seedKey}] schedule.cron='${cron}' is not a valid cron expression: ${message}`,\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// 6. Definition schema (Zod-validated structural shape) (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Structural schema for a {@link DataSeedDefinition}. Used by the factory\n * for first-line parse validation; mode-specific cross-field rules and the\n * §3.6 trigger × mode matrix are enforced by the factory **after** the\n * structural parse (see {@link createDataSeedDefinition}).\n */\nexport const DataSeedDefinitionSchema = z.object({\n /**\n * Stable identifier of this seed across deploys. Convention: kebab-case\n * segments joined by `:`, e.g. `\"system-emails:welcome\"`. Documented but\n * not regex-enforced in v1 — Phase 2 may add a lint.\n */\n seedKey: z.string().min(1),\n\n /** Where this seed materializes. */\n scope: DataSeedScopeRefSchema,\n\n /** The target resource whose rows the seed produces / updates. */\n resourceType: z.string().min(1),\n\n /**\n * Triggers that fire this seed. Required and must satisfy the §3.6 matrix\n * (relative to `mode`) for every mode EXCEPT `LAZY`.\n *\n * For `mode === LAZY`, authors MUST omit this field — the factory\n * auto-injects exactly one `LAZY_ACCESS` trigger from the\n * {@link DataSeedLazyAccessConfigSchema} block (Phase 5 audit\n * F-2/F-12/F-18: single declaration site for lazy-access wiring).\n * Declaring `triggers` explicitly for a LAZY seed is rejected at\n * factory time with an actionable error.\n */\n triggers: z.array(DataSeedTriggerSchema).optional(),\n\n /** Reconciliation mode (see {@link DataSeedMode}). */\n mode: z.enum(DataSeedMode),\n\n /**\n * Per-seed monotonic version. Bumping this is the developer's signal to\n * the dispatcher: \"re-run me on existing scopes to apply changes\". The\n * `DataSeedAppliedVersionsSummary` cache (Phase 2) makes the version-gate\n * check O(1) at boot.\n */\n version: z.number().int().positive(),\n\n /**\n * Dotted-path field names the seed owns.\n *\n * Optional to write, defaulting to `[]`. Because the authoring type is\n * derived from the schema's INPUT shape ({@link DataSeedDefinitionAuthored})\n * while consumers read the OUTPUT shape, authors may omit this field and the\n * dispatcher still reads a guaranteed `string[]`.\n *\n * Declare it when `mode in {UPGRADE, SYNC}` — `UPGRADE` rejects an empty list\n * at factory time, since an upgrade with nothing to re-apply is a no-op.\n *\n * OMIT it for `LAZY` and `ADDITIVE`. Although every `ADDITIVE` pass re-stamps\n * the declared payload onto existing rows (audit L2-6 — see\n * {@link DataSeedMode} JSDoc), the writer does NOT consult `managedFields` on\n * that mode: the full declared payload is the source of truth. Naming fields\n * there would imply an ownership boundary the dispatcher does not enforce.\n * Use `UPGRADE` if you need version-gated re-stamping that preserves user\n * customization on non-managed columns.\n */\n managedFields: z.array(z.string()).default([]),\n\n /**\n * Static entries (mutually exclusive with `entry` — enforced by factory).\n * Pre-known set of rows; typical for Level-A `ADDITIVE` / `UPGRADE` /\n * `SYNC` seeds (system templates, role catalog, country codes, …).\n */\n entries: z.array(DataSeedStaticEntrySchema).optional(),\n\n /**\n * Opt-in: inject the resolved scope foreign key into every STATIC entry's\n * `businessKey` and `data` at dispatch time.\n *\n * Level-A (application-lifecycle) seeds have no triggering row, so a\n * scope-anchored singleton (e.g. `applicationSsoConfig`, whose FK column is\n * `applicationId`) cannot hard-code its owner's `_id` at declaration time the\n * way a Level-B `entry(triggeringRow)` can. Setting this flag makes the\n * dispatcher materialise the scope FK (from the canonical\n * `coreResourceShared_FieldIdentifier` map, e.g. APPLICATION → `applicationId`)\n * onto each entry — the symmetric analogue of a Level-B entry hand-writing its\n * FK. Authors then leave the FK OUT of the static `businessKey`/`data`.\n *\n * OFF by default: existing Level-A static seeds (e.g. a system-email-template\n * catalog keyed by `templateKey`, not by the app FK) are unaffected. FORBIDDEN\n * for `scope.kind === GLOBAL` (no owning scope → no FK) — rejected at factory\n * time.\n *\n * `.optional()` (not `.default(false)`) so consumers can distinguish \"never\n * declared\" from an explicit `false`; they treat `undefined` as `false`.\n *\n * Historical note: this was originally `.optional()` to dodge a defect in the\n * authoring type — `DataSeedDefinitionInput` was derived from\n * `DataSeedDefinitionParsed` (`z.infer`, the OUTPUT shape), where a `.default()`\n * field becomes REQUIRED on the author input. That defect is fixed: the\n * authoring type is now {@link DataSeedDefinitionAuthored} (`z.input`), so\n * `.default(...)` no longer forces authors to write the field. Switching this\n * flag to `.default(false)` would now be safe on the authoring side, but it\n * would still narrow the output type and remove the tri-state above, so it is\n * deliberately left as-is.\n */\n anchorEntriesToScopeForeignKey: z.boolean().optional(),\n\n /** Optional rename ledger to preserve identity across business-key renames. */\n renames: z.array(DataSeedRenameSchema).optional(),\n\n /** Optional cross-seed dependency declarations (DAG resolved by Phase 2). */\n dependsOn: z.array(DataSeedRefSchema).optional(),\n\n /**\n * Required when `mode === SYNC` (D3 — destructive semantics must be\n * declared, never defaulted).\n */\n onOrphan: z.enum(DataSeedSyncOrphanPolicy).optional(),\n\n /**\n * How the reconcile loop decides \"already applied\" at version equality (PR-30, partial-resource\n * paper). Absent = VERSION (the historical contract, byte-identical). External-data pipeline seeds declare\n * CONTENT: their rows change remotely with no version bump, so change detection compares the\n * declared values against the persisted row at the managed paths.\n */\n reconcileStrategy: z.enum(DataSeedReconcileStrategy).optional(),\n\n /**\n * `ADDITIVE` only: what a pass does to a row that already exists. Absent =\n * {@link DataSeedExistingRowTreatment.REAPPLY_DECLARED_PAYLOAD}, the historical contract.\n * FORBIDDEN on every other mode (factory-enforced) — UPGRADE and SYNC already scope their writes\n * through `managedFields`, and LAZY never touches an existing row.\n */\n existingRowTreatment: z.enum(DataSeedExistingRowTreatment).optional(),\n\n /**\n * Required when (and only when) `mode === DataSeedMode.LAZY`. Carries\n * the lazy-access binding `(onResource, onOperation)` from which the\n * factory auto-injects a matching `LAZY_ACCESS` trigger. Authors of\n * non-LAZY seeds MUST omit this — declaring it on a non-LAZY seed is\n * rejected by the factory.\n */\n lazyAccess: DataSeedLazyAccessConfigSchema.optional(),\n\n /**\n * Optional cron-driven re-run binding (Phase 7, Item 47). Presence\n * of this block is the SOLE opt-in signal the\n * `data-seeding:scheduled-rerun` engine batch consults — seeds\n * without `schedule?` are NEVER picked up by the cron path.\n *\n * Mode incompatibilities (factory-enforced):\n *\n * - `mode === LAZY` + `schedule` is rejected — periodic re-runs\n * contradict LAZY's \"first access creates the row, never\n * re-applied\" contract.\n *\n * Cross-field invariants (factory-enforced):\n *\n * - `schedule.iterationStrategy` must be legal for `scope.kind`\n * per {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}.\n * - `schedule.cron` must parse via `cron-parser`.\n * - When `scopeIteration?` is ALSO declared, the two strategies\n * MUST agree — see {@link validateScopeIterationVsScheduleConsistency}.\n */\n schedule: DataSeedScheduleSchema.optional(),\n\n /**\n * Optional top-level scope-iteration declaration (Phase 7, Item 51 —\n * Slice 1, future-readiness).\n *\n * **Status — schema surface only**: this block is parsed and\n * validated at factory time, but the runtime Level-A non-cron path\n * still throws `NotImplemented` for ORG/USER/CUSTOM-scoped seeds\n * (`assertSeedIsSupportedForLevelA`). The schema lives in place so\n * that when Item 51's engine wiring lands (deferred until a real\n * consumer surfaces — see\n * `.cursor/plans/seeding-phase-7-multitenant-upgrade-sync.md`) the\n * declaration site is already canonical and Item 47's cron batch\n * can switch its read site from {@link DataSeedSchedule.iterationStrategy}\n * to {@link DataSeedScopeIteration.strategy} in a single targeted\n * edit, with no risk of silent divergence thanks to the\n * factory-time drift guard\n * ({@link validateScopeIterationVsScheduleConsistency}).\n *\n * Cross-field invariants (factory-enforced — see\n * {@link validateScopeIterationAgainstScope}):\n *\n * - FORBIDDEN when `scope.kind ∈ {GLOBAL, APPLICATION}` (single-anchor\n * scopes — there is nothing to iterate).\n * - `scopeIteration.strategy` must be legal for `scope.kind`\n * per {@link ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND}.\n * - When `schedule?` is ALSO declared, strategy + customResolverKey\n * on both blocks MUST agree (drift guard).\n *\n * NOT-required-for-now: ORG/USER/CUSTOM Level-A seeds may parse\n * successfully WITHOUT this block — the runtime gate from\n * `assertSeedIsSupportedForLevelA` remains the single source of\n * \"not supported\" truth at the engine layer until Item 51 Slice 2\n * lifts it. Forcing required-ness today would mislead authors into\n * adding a field they cannot use at runtime.\n */\n scopeIteration: DataSeedScopeIterationSchema.optional(),\n\n /**\n * The runtime environments this seed may run in. OMITTED means every environment — the posture of\n * every reference seed (a role catalog, a closed list, a system template), which the application\n * needs wherever it runs.\n *\n * Declare it to keep DEMONSTRATION data out of production (#2234): a seed that fills a freshly\n * created application with sample customers is wanted in `development` and `staging` and never in\n * `production`, where those rows would sit beside real ones. A restricted seed is withheld at\n * registration, before any trigger, schedule or lazy-access path can reach it, so no dispatch path\n * runs it outside the listed environments.\n *\n * Fails CLOSED: on a deployment that declares no environment, a restricted seed is withheld (an\n * undeclared environment is never assumed to be development — see `readDeclaredApplicationEnvironment`).\n * At least one environment: an empty list would declare a seed that can never run.\n */\n allowedApplicationEnvironments: z.array(z.enum(RuntimeEnvironment)).min(1).optional(),\n}).strict();\n\n/**\n * Inferred runtime (**output**) shape of {@link DataSeedDefinitionSchema},\n * without the non-Zod-modelable `entry` function and `migrators` map.\n * {@link DataSeedDefinition} extends this with those captured fields.\n *\n * Every field carrying a Zod `.default(...)` is REQUIRED here — by the time a\n * definition has this type the parser has already materialized the default. Do\n * not use this type to describe what an author writes; see\n * {@link DataSeedDefinitionAuthored}.\n */\nexport type DataSeedDefinitionParsed = z.infer<typeof DataSeedDefinitionSchema>;\n\n/**\n * Authoring (**input**) shape of {@link DataSeedDefinitionSchema} — what a\n * developer literally types at a declaration site, before parsing.\n *\n * The distinction from {@link DataSeedDefinitionParsed} is not academic. A field\n * declared with `.default(...)` is OPTIONAL on the way in and GUARANTEED on the\n * way out, so the two types genuinely differ and each one is correct for exactly\n * one audience. Deriving the authoring surface from the output type (as this\n * contract originally did) silently forces authors to hand-write every defaulted\n * field — which is how `managedFields: []` became mandatory boilerplate on every\n * `ADDITIVE` and `LAZY` seed, for a value the writer never reads on those modes.\n */\nexport type DataSeedDefinitionAuthored = z.input<typeof DataSeedDefinitionSchema>;\n\n/** @wildo_source:part:start saas.data-seeding.definition-input facet:kind:framework-api facet:audience:app-developer */\n/**\n * Authoring-time input shape passed to {@link createDataSeedDefinition}.\n * Adds the function-typed fields that Zod cannot validate — they are\n * passed through verbatim by the factory.\n *\n * `triggers` is optional only for LAZY seeds: the factory derives one trigger\n * from `lazyAccess`. Non-LAZY seeds must declare their trigger explicitly so\n * startup never guesses when a mutation is admissible.\n *\n * Required-vs-optional summary (cross-mode):\n *\n * - `seedKey`, `scope`, `resourceType`, `mode`, `version` — always required.\n * - `entries` xor `entry` — exactly one (static array vs computed\n * function). Computed `entry` requires at least one Level-B trigger.\n * - `managedFields` — OPTIONAL to write; defaults to `[]`. Required\n * non-empty for `UPGRADE` (the factory rejects an empty list, whether\n * you wrote it or inherited the default), recommended for `SYNC`.\n * OMIT it for `ADDITIVE` and `LAZY`: those modes never consult the\n * list — `ADDITIVE` re-stamps the FULL declared payload on every pass,\n * bypassing the projection entirely — so writing `managedFields: []`\n * there is noise that implies an ownership boundary the dispatcher\n * does not enforce.\n * - `triggers` — required for non-LAZY modes (≥1 trigger), MUST be\n * omitted for LAZY (factory auto-injects from `lazyAccess`).\n * - `lazyAccess` — required for LAZY, FORBIDDEN otherwise.\n * - `onOrphan` — required for SYNC, forbidden otherwise.\n * - `existingRowTreatment` — ADDITIVE only. Declare `FILL_MISSING_DEFAULTS`\n * when the seed CREATES a row a person owns afterwards (a config\n * singleton): the default re-applies the declared payload on every pass,\n * which replaces whole stored blocks.\n * - `renames` — optional rename ledger; each `rename.field` MUST be a\n * business-key field and `rename.to` MUST appear as a declared\n * business-key value (F-3 + F-10).\n * - `migrators` — optional per-version migrator map for UPGRADE; the\n * dispatcher walks `fromVersion` from the persisted row's\n * `seedVersion` up to `version - 1`, then re-applies\n * `managedFields` from the declared entries.\n * - `dependsOn` — optional cross-seed dependency declarations (DAG\n * resolved at dispatch time).\n * - `schedule` — optional cron-driven re-run binding; presence is the\n * sole opt-in signal for the engine cron batch. FORBIDDEN for\n * `mode === LAZY`.\n * - `allowedApplicationEnvironments` — optional; omit for reference data the\n * application needs everywhere. DEMONSTRATION data (sample rows that make a\n * fresh application look lived-in) MUST declare it without `production`, e.g.\n * `[RuntimeEnvironment.DEVELOPMENT, RuntimeEnvironment.STAGING]` (import\n * `RuntimeEnvironment` from `@wildo-ai/saas-models`). A restricted seed is never\n * registered elsewhere, and is withheld when the deployment declares no environment.\n */\nexport interface DataSeedDefinitionInput extends DataSeedDefinitionAuthored, DataSeedDefinitionCallbacks {}\n\n/**\n * The two function-typed fields Zod cannot model or validate. The factory\n * captures them before the structural parse and re-attaches them to its result\n * verbatim, so they appear identically on the authoring and runtime shapes.\n */\nexport interface DataSeedDefinitionCallbacks {\n /**\n * Computed entry generator (mutually exclusive with `entries`). Typical\n * for Level-B seeds that need data from the triggering row\n * (`afterCreate` of an Organization → spawn its default Workspace).\n */\n entry?: DataSeedComputedEntryFn;\n /**\n * Per-version migrators applied sequentially from the persisted row's\n * recorded `seedVersion` up to `version - 1`. After all applicable\n * migrators run the dispatcher then re-applies the declared\n * `managedFields` from the seed entries.\n */\n migrators?: {\n fromVersion: Record<number, DataSeedMigratorFn>;\n };\n}\n\n/**\n * Runtime shape returned by {@link createDataSeedDefinition} and consumed\n * by the registry / dispatcher.\n *\n * This is built on the PARSED (output) shape, not on\n * {@link DataSeedDefinitionInput}, and the difference is load-bearing: every\n * defaulted field is guaranteed present here because the factory returns the\n * parser's result. Dispatcher and registry consumers can therefore read\n * `seed.managedFields` as a plain `string[]` — no `?? []` guard — even when the\n * author omitted it.\n *\n * `triggers` is additionally narrowed to GUARANTEED non-empty: the LAZY branch\n * is auto-populated by the factory and every other branch enforces explicit\n * declaration, so consumers can call `seed.triggers.some(...)` without a\n * defensive guard.\n */\nexport interface DataSeedDefinition extends DataSeedDefinitionParsed, DataSeedDefinitionCallbacks {\n triggers: DataSeedTrigger[];\n}\n/** @wildo_source:part:end saas.data-seeding.definition-input */\n\n// ---------------------------------------------------------------------------\n// 7. Factory (Task 1.3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Author a {@link DataSeedDefinition}.\n *\n * Validation pipeline:\n * 1. **Structural parse** via {@link DataSeedDefinitionSchema}. Catches\n * missing fields, wrong types, malformed enums.\n * 2. **§3.6 trigger × mode matrix** via\n * {@link validateDataSeedTriggerModeCombination} — see\n * `data-seed-trigger-mode-matrix.shared.utils.ts`. Same rule consulted\n * by the spec layer (`saas-specifications/.../data-seed-specification.schemas.ts`)\n * so authoring and specification cannot drift.\n * 3. **Mode-specific structural rules** below — these encode constraints\n * that cannot be expressed by the structural schema alone (e.g.\n * \"`SYNC` requires `onOrphan`\", \"exactly one of `entries`/`entry`\").\n *\n * On success returns the input verbatim — including the function-typed\n * `entry` and `migrators` fields (which Zod cannot validate).\n *\n * On failure throws a single descriptive `Error` whose message names the\n * failing `seedKey`, the failing rule, and (when relevant) the §3.6 cell.\n */\nexport function createDataSeedDefinition(\n definition: DataSeedDefinitionInput,\n): DataSeedDefinition {\n // (1) Structural parse — strip the function-typed fields before parse,\n // re-attach after, because `z.object().strict()` would reject them.\n //\n // L1-2 (post-Phase-7 close-out): wrap structural failures with the\n // `[DataSeed:<seedKey>] …` prefix that every other validator in\n // this factory emits, so authors get a uniformly grep-able identity\n // across every error class. The original `ZodError` is kept on the\n // `cause` chain so tooling can still introspect the structured\n // issues. We extract `seedKey` defensively from the input bag (which\n // is `unknown`-shaped before the parse succeeds) so a malformed\n // input that omitted `seedKey` still produces a usable message.\n const { entry, migrators, ...structural } = definition;\n let parsed: DataSeedDefinitionParsed;\n try {\n parsed = DataSeedDefinitionSchema.parse(structural);\n } catch (parseError) {\n const probableSeedKey = typeof (definition as { seedKey?: unknown }).seedKey === 'string'\n ? (definition as { seedKey: string }).seedKey\n : '<unknown>';\n const message = describeCaughtError(parseError);\n const wrapped = new Error(\n `[DataSeed:${probableSeedKey}] structural validation failed (Zod parse): ${message}`,\n );\n (wrapped as Error & { cause?: unknown }).cause = parseError;\n throw wrapped;\n }\n\n // (2) Lazy-access auto-derivation (Phase 5 audit F-2/F-12/F-18).\n // For LAZY mode the `lazyAccess` block is the SINGLE declaration\n // site for the trigger; the factory materializes the matching\n // `LAZY_ACCESS` trigger here so the registry's by-trigger-kind\n // index keeps working without authors having to re-declare the\n // same data. For non-LAZY modes the explicit `triggers` array\n // remains the only source of truth.\n const triggers = resolveDeclaredTriggers(parsed);\n\n // (3) Trigger × mode matrix — single source of truth lives in the shared\n // utility so saas-models and saas-specifications cannot diverge.\n for (const trigger of triggers) {\n validateDataSeedTriggerModeCombination({\n seedKey: parsed.seedKey,\n mode: parsed.mode,\n trigger,\n });\n }\n\n // (4) Mode-specific structural rules.\n validateEntriesVsEntry(parsed.seedKey, parsed.entries, entry);\n validateModeSpecificFields({ ...parsed, triggers }, entry);\n // (5) Rename invariants (F-3 + F-10 — Phase-4 deep review). Static\n // `entries` are required for the cross-check; computed `entry`\n // defers the same checks to dispatcher runtime (registered\n // resource schema can be consulted there). Skipping when\n // `entries` is absent matches the same pragmatism applied to\n // `validateEntriesVsEntry` above.\n if (parsed.renames !== undefined && parsed.entries !== undefined) {\n validateRenameInvariants(parsed.seedKey, parsed.renames, parsed.entries);\n }\n // (6) Schedule cross-field invariants (Phase 7, Item 47). Both\n // checks are no-ops when `schedule?` is absent — cron-aware\n // declaration is opt-in.\n if (parsed.schedule !== undefined) {\n validateDataSeedScheduleCronExpression(parsed.seedKey, parsed.schedule.cron);\n validateDataSeedScheduleAgainstScope(parsed.seedKey, parsed.scope.kind, parsed.schedule);\n }\n // (7) ScopeIteration cross-field invariants (Phase 7, Item 51 —\n // Slice 1, future-readiness). Forbidden on GLOBAL/APPLICATION\n // (single-anchor scopes) and must align with the same\n // ALLOWED_ITERATION_STRATEGIES_BY_SCOPE_KIND matrix the cron\n // path consults; when both `scopeIteration` and `schedule` are\n // declared, the drift guard rejects mismatched strategies. The\n // runtime engine still throws NotImplemented for ORG/USER/CUSTOM\n // Level-A — see `assertSeedIsSupportedForLevelA` — so the\n // factory deliberately does NOT require this field, only\n // validates it when present.\n if (parsed.scopeIteration !== undefined) {\n validateScopeIterationAgainstScope(parsed.seedKey, parsed.scope.kind, parsed.scopeIteration);\n if (parsed.schedule !== undefined) {\n validateScopeIterationVsScheduleConsistency(\n parsed.seedKey,\n parsed.scopeIteration,\n parsed.schedule,\n );\n }\n }\n // (8) Migrators key invariants (L1-3, post-Phase-7 close-out). Each\n // `migrators.fromVersion` key MUST be a positive integer\n // STRICTLY less than `parsed.version` so the dispatcher's\n // \"walk fromVersion+1 .. version\" loop has a defined endpoint\n // and no migrator is unreachable. Without this guard a typo\n // like `fromVersion: { 5: ... }` on a `version: 3` seed would\n // silently never run.\n if (migrators !== undefined) {\n validateMigratorsFromVersionKeys(parsed.seedKey, parsed.version, migrators);\n }\n\n // (9) Scope-FK injection opt-in (`anchorEntriesToScopeForeignKey`). Only\n // meaningful for a per-row scope that owns an FK column; a GLOBAL seed\n // has no owning scope, so the flag is a contradiction — fail fast rather\n // than silently no-op at dispatch.\n if (parsed.anchorEntriesToScopeForeignKey && parsed.scope.kind === DataSeedScopeKind.GLOBAL) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`anchorEntriesToScopeForeignKey\\` is set but \\`scope.kind\\` is GLOBAL — GLOBAL seeds have no owning scope FK to inject. Remove the flag or choose a per-row scope (APPLICATION / ORGANIZATION / USER / CUSTOM).`,\n );\n }\n\n return { ...parsed, triggers, entry, migrators };\n}\n\n/**\n * Migrators-key validator (L1-3, post-Pass-2 close-out fix:\n * Cat-2 — allow `fromVersion[0]`).\n *\n * Reject any `fromVersion` key that is not a NON-NEGATIVE integer\n * STRICTLY LESS THAN the declared `version`. Two failure modes:\n *\n * - **Negative / non-integer** keys (`-1`, `'1.5'`, `'abc'`) are\n * typos — string-keyed objects can collect anything at runtime,\n * so the validator has to re-parse with `Number()` and gate on\n * `Number.isInteger() && >= 0`.\n * - **Keys `>= declaredVersion`** are unreachable migrators. The\n * dispatcher's `runDataSeedMigratorChain` walks\n * `for v = persistedVersion .. declaredVersion - 1`, so any\n * `fromVersion[v]` with `v >= declaredVersion` never fires.\n *\n * **`fromVersion[0]` is intentionally legal** (audit Cat-2,\n * post-Pass-2 close-out): when a row was authored before any\n * `__seed.seedVersion` stamping (e.g. data backfilled before the\n * seed was registered, or an early-stage prototype that started at\n * v0), the migrator runner reads `persistedVersion: 0` and looks\n * up `migrators.fromVersion[0]` to upgrade it to v1. The earlier\n * Pass-1 validator rejected `numericKey <= 0` and silently broke\n * this v0 → v1 path; Pass-2 close-out relaxes the gate to\n * `numericKey < 0`.\n */\nfunction validateMigratorsFromVersionKeys(\n seedKey: string,\n declaredVersion: number,\n migrators: NonNullable<DataSeedDefinitionInput['migrators']>,\n): void {\n for (const rawKey of Object.keys(migrators.fromVersion)) {\n const numericKey = Number(rawKey);\n if (!Number.isInteger(numericKey) || numericKey < 0) {\n throw new Error(\n `[DataSeed:${seedKey}] \\`migrators.fromVersion\\` key \"${rawKey}\" must be a non-negative integer (got \"${rawKey}\").`,\n );\n }\n if (numericKey >= declaredVersion) {\n throw new Error(\n `[DataSeed:${seedKey}] \\`migrators.fromVersion\\` key ${numericKey} is unreachable: it must be strictly less than the declared seed version (${declaredVersion}). The dispatcher walks \\`v = persistedVersion .. version - 1\\`; a key ≥ version would never fire.`,\n );\n }\n }\n}\n\n/**\n * Resolve the runtime `triggers[]` array, applying the LAZY auto-derivation\n * contract (Phase 5 audit F-2/F-12/F-18).\n *\n * Rules:\n * - `mode === LAZY` MUST have `lazyAccess` AND MUST NOT declare\n * `triggers` explicitly. The factory injects exactly one\n * `LAZY_ACCESS` trigger from `lazyAccess.{onResource,onOperation}`.\n * - `mode !== LAZY` MUST declare `triggers` (≥1) AND MUST NOT declare\n * `lazyAccess`. The trigger array is returned verbatim.\n *\n * The \"MUST NOT\" branches are the divergence-prevention surface: if both\n * `lazyAccess` and `triggers` are declared, the engine would have to\n * pick one, and the unused declaration would silently rot. We reject at\n * authoring time so the bug surfaces immediately.\n */\nfunction resolveDeclaredTriggers(\n parsed: DataSeedDefinitionParsed,\n): DataSeedTrigger[] {\n if (parsed.mode === DataSeedMode.LAZY) {\n if (parsed.lazyAccess === undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=LAZY requires a \\`lazyAccess\\` configuration block (the single declaration site for the lazy-access trigger).`,\n );\n }\n if (parsed.triggers !== undefined && parsed.triggers.length > 0) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=LAZY auto-derives the trigger from \\`lazyAccess\\` — do NOT also declare \\`triggers\\` (Phase 5 audit F-2/F-12/F-18: single declaration site).`,\n );\n }\n return [{\n triggerKind: DataSeedTriggerKind.LAZY_ACCESS,\n resourceType: parsed.lazyAccess.onResource,\n operation: parsed.lazyAccess.onOperation,\n }];\n }\n\n // Non-LAZY modes.\n if (parsed.lazyAccess !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`lazyAccess\\` is only valid when mode=LAZY (got mode=${parsed.mode}).`,\n );\n }\n if (parsed.triggers === undefined || parsed.triggers.length === 0) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=${parsed.mode} requires at least one trigger.`,\n );\n }\n return parsed.triggers;\n}\n\n/**\n * Exactly one of `entries` (static) or `entry` (computed) must be provided.\n * Zod cannot express \"XOR of two optional fields\" cleanly without an opaque\n * `.refine` — keeping it as an explicit validator keeps the error message\n * actionable.\n */\nfunction validateEntriesVsEntry(\n seedKey: string,\n entries: DataSeedStaticEntry[] | undefined,\n entry: DataSeedComputedEntryFn | undefined,\n): void {\n const hasEntries = entries !== undefined && entries.length > 0;\n const hasEntry = entry !== undefined;\n if (hasEntries && hasEntry) {\n throw new Error(\n `[DataSeed:${seedKey}] Provide exactly one of \\`entries\\` (static array) or \\`entry\\` (computed function), not both.`,\n );\n }\n if (!hasEntries && !hasEntry) {\n throw new Error(\n `[DataSeed:${seedKey}] One of \\`entries\\` (static array) or \\`entry\\` (computed function) is required.`,\n );\n }\n}\n\n/**\n * Mode-specific cross-field rules. Each branch guards exactly one §6 design\n * decision so the failure messages map 1:1 to the design doc.\n *\n * Operates on the **resolved** triggers (post-{@link resolveDeclaredTriggers})\n * so the LAZY auto-injected trigger is included in cross-field checks.\n */\nfunction validateModeSpecificFields(\n parsed: DataSeedDefinitionParsed & { triggers: DataSeedTrigger[] },\n entry: DataSeedComputedEntryFn | undefined,\n): void {\n // D3 — `SYNC` requires explicit `onOrphan`.\n if (parsed.mode === DataSeedMode.SYNC && parsed.onOrphan === undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=SYNC requires an explicit \\`onOrphan\\` policy (D3 — destructive semantics must be declared).`,\n );\n }\n // L1-1 (post-Phase-7 close-out) — symmetric guard: `onOrphan` is\n // FORBIDDEN outside SYNC. The `DataSeedDefinitionInput` JSDoc\n // (line \"`onOrphan` — required for SYNC (D3), FORBIDDEN otherwise\")\n // is the contract; before this check the factory only enforced the\n // \"required\" half, so an author who left a stray `onOrphan: 'delete'`\n // on a non-SYNC seed (e.g. after refactoring SYNC → UPGRADE) would\n // see the field SILENTLY IGNORED — exactly the \"stray field rotting\"\n // failure mode the trigger × mode matrix is designed to prevent.\n // Rejecting here surfaces the bug at authoring time so the author\n // either drops the field or flips the mode back to SYNC.\n if (parsed.mode !== DataSeedMode.SYNC && parsed.onOrphan !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`onOrphan\\` is only valid when mode=SYNC (got mode=${parsed.mode}); drop the field or change \\`mode\\` to SYNC.`,\n );\n }\n // `existingRowTreatment` answers a question only ADDITIVE asks — see the enum JSDoc. Refused\n // elsewhere for the same reason as a stray `onOrphan`: a field the writer never reads would sit on\n // the declaration looking like a guarantee.\n if (parsed.mode !== DataSeedMode.ADDITIVE && parsed.existingRowTreatment !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] \\`existingRowTreatment\\` is only valid when mode=ADDITIVE (got mode=${parsed.mode}); drop the field or change \\`mode\\` to ADDITIVE.`,\n );\n }\n // `UPGRADE` without managed fields is a no-op — the dispatcher would\n // have nothing to write. Guard now to surface the bug at authoring time.\n if (parsed.mode === DataSeedMode.UPGRADE && parsed.managedFields.length === 0) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=UPGRADE requires a non-empty \\`managedFields\\` list (otherwise the upgrade has nothing to apply).`,\n );\n }\n // The Phase 5 audit F-2/F-12/F-18 contract — `mode === LAZY` ⇒\n // exactly one auto-injected `LAZY_ACCESS` trigger — is enforced by\n // {@link resolveDeclaredTriggers} above; no duplicate check needed\n // here.\n // Computed `entry` only makes sense with at least one Level-B trigger.\n if (entry !== undefined && parsed.triggers.every(isLevelADataSeedTrigger)) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] computed \\`entry\\` requires at least one Level-B trigger ('resourceLifecycle' or 'lazyAccess'); use static \\`entries\\` for Level-A only seeds.`,\n );\n }\n // Phase 7 / Item 47 — N10: LAZY + schedule is semantically\n // contradictory. LAZY's contract is \"first access creates the row,\n // never re-applied\" (P5-C=C1 idempotency cache). A periodic\n // re-run would either be a no-op (the LAZY ledger row already\n // exists) or require deleting prior COMPLETED rows on every\n // tick — both wrong. Authors who want periodic refresh of LAZY-\n // shaped data should declare a SYNC seed with a `schedule` block\n // instead.\n if (parsed.mode === DataSeedMode.LAZY && parsed.schedule !== undefined) {\n throw new Error(\n `[DataSeed:${parsed.seedKey}] mode=LAZY + \\`schedule\\` is not allowed (LAZY's contract is \"first access creates the row, never re-applied\"; periodic re-runs are semantically incompatible). Declare a SYNC seed with a \\`schedule\\` instead if you need periodic refresh.`,\n );\n }\n}\n\n/**\n * Cross-check `renames[]` against declared static `entries[]` to catch\n * authoring typos at definition time rather than at the next pass —\n * F-3 + F-10 (Phase-4 deep review).\n *\n * --- F-10: `rename.field` MUST be part of the business-key shape ---\n *\n * Renames are a business-key relocation tool: their entire point is\n * to move a row from one business-key value to another so that the\n * dispatcher's `findByBusinessKey(...)` lookup re-locates the existing\n * row on the next pass. If `rename.field` isn't a business-key field,\n * the rename rewrites a non-identifying column instead — silently a\n * no-op for identity, and the next pass will INSERT a duplicate at\n * the new declared key. We detect this here by requiring that every\n * static entry's `businessKey` shape includes `rename.field`.\n *\n * Skipped when `entries` is absent (computed `entry` — runtime check).\n *\n * --- F-3: `rename.to` MUST be a declared value for `rename.field` ---\n *\n * The rename pipeline rewrites `field=from → field=to`, then the\n * reconcile loop expects to find rows at every declared business key.\n * If `to` doesn't appear as the value of `rename.field` on any declared\n * entry, the rename's only effect is to corrupt the row's business\n * key into a value the seed no longer authors — and on the next pass\n * the same row becomes an orphan (SYNC) or an unowned legacy row\n * (UPGRADE/ADDITIVE). Almost always a typo (`'new-namee'` vs\n * `'new-name'`); cheap to catch at authoring time.\n */\nfunction validateRenameInvariants(\n seedKey: string,\n renames: readonly DataSeedRename[],\n entries: readonly DataSeedStaticEntry[],\n): void {\n for (const rename of renames) {\n // F-10 — every entry's businessKey must include `rename.field`.\n for (const entry of entries) {\n if (!(rename.field in entry.businessKey)) {\n throw new Error(\n `[DataSeed:${seedKey}] rename.field '${rename.field}' is not part of the business key declared on entries[*].businessKey (F-10) — renames must target a business-key field, otherwise the rename silently no-ops and the next pass would insert a duplicate.`,\n );\n }\n }\n // F-3 — `rename.to` must appear as a declared value for that field.\n const declaredValuesForField = new Set<unknown>();\n for (const entry of entries) {\n const v = entry.businessKey[rename.field];\n if (v !== undefined) declaredValuesForField.add(v);\n }\n if (!declaredValuesForField.has(rename.to)) {\n throw new Error(\n `[DataSeed:${seedKey}] rename.to='${rename.to}' for field '${rename.field}' is not present on any declared entry's businessKey (F-3) — likely a typo. Declared values: [${[...declaredValuesForField].map(String).join(', ')}].`,\n );\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-seed-application-bootstrap.backend.d.ts","sourceRoot":"","sources":["../../../../../src/data-seeding/services/data-seed-application-bootstrap.backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"data-seed-application-bootstrap.backend.d.ts","sourceRoot":"","sources":["../../../../../src/data-seeding/services/data-seed-application-bootstrap.backend.ts"],"names":[],"mappings":"AAoBA,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,iEAAiE,CAAC;AAMzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAClG,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,6EAA6E,CAAC;AAClI,OAAO,KAAK,EACV,qBAAqB,EACrB,2CAA2C,EAC5C,MAAM,4FAA4F,CAAC;AACpG,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,+EAA+E,CAAC;AACtI,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,6FAA6F,CAAC;AAEzJ,mHAAmH;AACnH;;;;;GAKG;AACH,MAAM,WAAW,iCAAiC;IAChD;;;;;OAKG;IACH,SAAS,EAAE,SAAS,kBAAkB,EAAE,GAAG,SAAS,CAAC;IAErD;;;;;;;;;;OAUG;IACH,sBAAsB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAEzE;;;;;OAKG;IACH,cAAc,EAAE,OAAO,CAAC;IAExB,MAAM,EAAE,aAAa,CAAC;IACtB,gFAAgF;IAChF,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAEvE,QAAQ,EAAE,8BAA8B,CAAC;IACzC;;;;;;;OAOG;IACH,qBAAqB,EAAE,2CAA2C,CAAC;IACnE,UAAU,EAAE,gCAAgC,CAAC;IAE7C;;;;;;;;OAQG;IACH,gBAAgB,EAAE,qCAAqC,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,IAAI,CACT,iCAAiC,EACjC,WAAW,GAAG,wBAAwB,GAAG,UAAU,GAAG,UAAU,GAAG,uBAAuB,GAAG,YAAY,GAAG,kBAAkB,CAC/H,GAAG;IACF;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,yBAAyB,EAAE,OAAO,CAAC;CAC7C,GACA,IAAI,CA2DN;AAED;;;;GAIG;AACH,wBAAsB,wCAAwC,CAC5D,KAAK,EAAE,IAAI,CAAC,iCAAiC,EAAE,gBAAgB,GAAG,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC,GACtG,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { CoreResourceOperation } from '@wildo-ai/saas-models';
|
|
18
18
|
import { ENGINE_OWNED_DATA_SEEDS } from '../../data-seeding/services/engine-owned-data-seeds.backend.js';
|
|
19
|
+
import { admitDataSeedsForApplicationEnvironment } from '../../data-seeding/services/data-seed-environment-admission.backend.js';
|
|
20
|
+
import { readDeclaredApplicationEnvironment } from '../../config/models/app-env-config.backend.schemas.js';
|
|
19
21
|
import { DataSeedResourceLifecyclePhase, DataSeedTriggerKind, } from '../../data-seeding/models/data-seed.shared.schemas.js';
|
|
20
22
|
import { DataSeedRunStatus } from '../../data-seeding/models/data-seed-run.shared.schemas.js';
|
|
21
23
|
import { describeCaughtError } from '../../errors/utils/error-log-level-classification.backend.utils.js';
|
|
@@ -86,7 +88,13 @@ export function wireDataSeedingDeclarations(input) {
|
|
|
86
88
|
* of the collision a reader can act on.
|
|
87
89
|
*/
|
|
88
90
|
const engineOwned = registersEngineOwnedSeeds ? ENGINE_OWNED_DATA_SEEDS : [];
|
|
89
|
-
|
|
91
|
+
/*
|
|
92
|
+
* Admitted over the MERGED list (#2234), so a restricted seed is withheld at registration whoever owns it — the
|
|
93
|
+
* promise `allowedApplicationEnvironments` makes. The application's own seeds were already admitted where
|
|
94
|
+
* `start-application` took them (its seeding gates read that set); re-admitting them here is idempotent, and it is
|
|
95
|
+
* what reaches the engine's.
|
|
96
|
+
*/
|
|
97
|
+
const seeds = admitDataSeedsForApplicationEnvironment([...engineOwned, ...(dataSeeds ?? [])], readDeclaredApplicationEnvironment(process.env)).admitted;
|
|
90
98
|
logDebug('🌱 Registering data-seed definitions...', {
|
|
91
99
|
count: seeds.length,
|
|
92
100
|
engineOwned: engineOwned.length,
|