System forms and startup maintenance
Keep source-defined system forms recoverable and diagnose migration integrity failures safely.
System form source synchronization
FormPlatform keeps editable form definitions in the management database. The forms listed below also have stable C# initializers so a new, empty database can recreate them. `tools/sync-system-forms.js` takes an explicit database-to-source snapshot of their current `schema_json` and Action Code.
Covered forms
The current Core source snapshot contains 31 forms:
| C# source | Database form names |
|---|---|
| `AppUsersForm.cs` | `AppUsersForm` |
| `FormCenterSystemForms.cs` | `Form_Center` |
| `FeatureSystemForms.cs` | `Feature_PersonalInfo` |
| `FormAccessSystemForms.cs` | `System_Form_Access` |
| `Licensing/LicenseSystemForm.cs` | `System_About` |
| `ModuleStarterSystemForm.cs` | `System_Module_Starter` |
| `SubmissionCenterSystemForms.cs` | `Submission_Center` |
| `SystemAccessSystemForms.cs` | `System_Users`, `System_Departments`, `System_Roles`, `System_User_Roles` |
| `SurveySystemForms.cs` | `Survey_Deployments`, `Survey_Lists`, `Survey_Deployment_Responses`, `Survey_Push_Notification`, `Survey_Respondents`, `Survey_Deployment`, `Survey_List`, `Survey_Respondent`, `Survey_Respondent_Login`, `Survey_Respondent_Dashboard`, `System_Notifications`, `Survey_Respondent_Reset_Password` |
| `SystemManagementForms.cs` | `ManageDataModels`, `ManageFormDataMappings`, `FormCenter` |
| `EnvironmentSurveyForms.cs` | `Survey_Environment` |
| `SystemLayoutForms.cs` | `SystemHeader`, `SystemLeftPane`, `SystemRightPane`, `SystemFooter` |
The synchronizer reports every database form not in this ownership list as `[no C# initializer]`. It never invents a Core owner for a user form or a separately installed module form.
Workflow
Run from the repository root against the trusted development database:
node tools/sync-system-forms.js --dry-run
node tools/sync-system-forms.js
node tools/sync-system-forms.js --dry-run
The final dry-run should report zero changed definitions. `--form <name>` and `--file <path>` can limit a synchronization. The script currently reads PostgreSQL through `psql`; it resolves the management connection using the same secure configuration order described in the deployment guide. Prefer User Secrets or deployment secret JSON instead of putting a connection string on the command line.
Review the complete Git diff after synchronization. This operation intentionally trusts the selected database: embedded Action Code becomes source code that the browser runtime can execute when the form loads.
What is restored
For a covered form, the snapshot writes:
- the complete form Schema, including component translations, `usedCssClasses`, and versioned `systemExtensions` already present in the database;
- Action Code when the C# definition has an Action Code raw-string slot, including an intentionally empty database value. The empty multiline raw string retains one blank content line so C# accepts the source while its value remains empty.
On an empty database, normal migrations and hosted initializers create the stable form IDs, names, descriptions, types, and standard metadata, publish the saved Schema, and then run any narrowly scoped compatibility repairs. The snapshot does not change an existing database when it is generated.
Survey overview system forms include one narrow compatibility repair: when an older DataGrid allows creation and already has an Edit form, has never stored `createShowType`, and the current source snapshot defines that default, startup copies the source value. `Survey_Deployments` currently declares `newWindow`, so upgraded and empty databases both create deployments through the detail Edit form. Other overview forms without a source default retain their legacy behavior, and an explicit Designer value such as `inline` is preserved.
`AppUsersFormInitializer` follows a strict create-if-missing rule: startup never overwrites an existing Designer Schema or metadata. The empty-database baseline maps to the Core-owned `SystemAccessUser` Data Model, and the initializer selects the property aliases actually present in the snapshotted Schema, including `userName`/`user_name`, `isActive`/`is_active`, and `departmentId`/`department_id`. The password control is always a write-only mapping to `passwordHash`; the server-side `SetFields @password` trigger hashes it, and the stored hash is never returned to the form.
Boundaries and recovery gaps
This is not a database backup and cannot by itself reproduce the complete current environment:
- user-created forms without C# initializers are not restored; `AppUsersForm` is now a Core baseline, but other ordinary user forms remain outside this coverage;
- form ACL rows, department ownership, audit timestamps, submission data, Survey business data, uploaded media, and module data are not embedded into source;
- metadata other than Action Code remains owned by initializer code. A Designer-only change to Data Mapping, triggers, anonymous publication, form type, feature dispatch, or another metadata field must also be represented in the relevant initializer before an empty database can reproduce it;
- Data Models and physical tables are recreated only when a migration or model initializer owns them. A user-imported Data Model is not recovered merely because a form Schema references it;
- forms belonging to an installed binary/private module must be snapshotted by that module's own source or migration, not copied into Core.
Survey business rows do not implicitly carry their target form definition. Supplemental deployment `01a09aa5-3614-7c8b-be9d-a2af3a1cb5fe` references the non-Core `surveyform1` definition with form ID `01a09a85-7a4f-7b2c-b837-b768e252e143`; the trusted definition is now restored by `SupplementalFormDefinitionInitializer` after the independently configured FormStorage schema exists. Its provider SQL is generated in `SupplementalInitializationMigration.cs`, but it is intentionally excluded from Management migration 017 so split data sources remain correct and the released checksum remains unchanged. If any deployment still references a missing form, assisted-administration endpoints fail closed with `409 survey.deploymentFormMissing`; the server never substitutes a placeholder or bypasses the target-form ACL.
`Survey_Respondent_Dashboard` uses the `respondent-dashboard-i18n-v3` incremental marker. The initializer adds missing Chinese translations only while the corresponding base property still equals the platform default; Designer-customized text and existing locale overrides are preserved. Dashboard API display tokens use explicit `@surveyStatus.*` and `@common.yes|no` references. ItemRenderer localizes those fields at render time and formats configured `dateTimeFields` through the browser `Intl.DateTimeFormat` for the active locale. The raw API values remain available, while formatted values are exposed as `<field>Text`. A server-paged ItemRenderer shows `noRecordLabel` only when no visible records exist; reaching the final page no longer produces a false empty-state message.
`Survey_Respondent_Login` uses the `respondent-login-i18n-v1` incremental marker. Its source Schema contains Chinese translations for the title and subtitle, user-name and password labels and placeholders, required-field messages, remember-me label, submit action, and notification pagination/empty state. Existing forms receive only missing translations whose corresponding base property still equals the source default, so Designer-customized text and existing locale overrides are preserved. Password-reset and external-provider controls are view-owned and continue to use the application message catalog in `RespondentLoginView.vue`; notification subjects and bodies remain user-authored content and are not machine-translated.
Use a real database backup for disaster recovery. Treat the C# snapshot as a reproducible baseline for code-owned forms, and test empty-database initialization in an isolated database before relying on a release package.
Chinese edition: SYSTEM_FORM_SOURCE_SYNC.zh-CN.md.
---
Startup migration error architecture
FormPlatform applies database migrations before normal application initialization. Each migration is identified by module and immutable migration name and is recorded with a checksum. This prevents a changed historical schema script from silently corrupting a database that already applied a previous version.
If startup reports that a migration was applied with a different checksum, do not update the checksum row or edit the old migration. Restore the released file from source control if it was accidentally changed, then create a new numbered migration for the desired schema change. For a disposable local test database, recreate the database only after confirming it contains no required data.
The application uses unified error responses for HTTP failures, but startup migration failures are intentionally fatal and visible in host logs: running an application against an uncertain schema is less safe than refusing to start. Verify the management connection string, database permissions, module package versions, and migration table before retrying.
Chinese edition: STARTUP_MIGRATION_ERROR_ARCHITECTURE.zh-CN.md.