Migrate ticket type INCIDENT to DAMAGE
AI-generated content
This document was generated by an AI assistant. Verify accuracy before relying on the details.
Angular 0.69 knows one damage type, TICKET_TYPE.DAMAGE; the data stores TICKET_TYPE.INCIDENT. On 0.69 every existing incident drops out of the « Sinistres » list, loses its damage card, and its Document.s tab never stops loading. Greg's decision (screen diff of 2026-10-03, FIX-LOTS § 3, answer 1): migrate the data — staging first, production at the 0.69 roll-out, and Greg reviews the dry-run output before anything is written. tools/scripts/migrate-incident-damage.mjs rewrites the code in the six places it is stored (tickets, ticket delegations, file metadata, two kinds of utilities configuration, the adb-views vacancy projection). It is dry-run by default, refuses a production-looking cluster unless told otherwise, writes one guarded $set per document, is idempotent, and logs every change so --rollback <log> can undo it.
At a glance
| Fact | Value |
|---|---|
| Script | tools/scripts/migrate-incident-damage.mjs (core + 52 unit tests in tools/scripts/lib / tools/scripts/test) |
| Runs as | an engineer, from a workstation — never from a Worker or CI |
| Default mode | dry-run: counts per collection/field, nothing written |
| Writes | updateOne + $set of whole top-level fields, guarded by _id and the exact values read. No insert, no delete, no replace, no index, no transaction |
| Amounts | never read, never written (details.* is untouched) |
| Log | write-ahead JSON Lines, --log <path> (default ./migrate-incident-damage-<timestamp>.jsonl) |
| Rollback | --rollback <log> (dry-run) then --rollback <log> --apply |
| Production guard | refused unless --i-know-this-is-prod; a host counts as non-production only if it is local or named like one (stage, dev, preprod, …) |
| Order | staging (adb-stage.cm0co.mongodb.net) first; production at the 0.69 roll-out |
| Volume on the committed seed | configurations: 4 nature rows, 35 event-type rows, 10 document rows (infra/seeds/data/adb-utilities/configurations.json) |
What it rewrites
| Family | db.collection.field | From → to | Why it must move |
|---|---|---|---|
ticket-configurations | adb-utilities.configurations.ticketType (TicketConfigurationEntity, TicketEventConfigurationEntity) | TICKET_TYPE.INCIDENT → TICKET_TYPE.DAMAGE | 0.69 asks the natures and event types of TICKET_TYPE.DAMAGE (create-ticket-modal.component.ts:435-436); the seed has them under INCIDENT only |
document-configurations | adb-utilities.configurations.documentType | FILE_TYPE.TICKET_INCIDENT → FILE_TYPE.TICKET_DAMAGE | the documents tab picks the type whose value is TICKET_DAMAGE (document-tab.component.ts:106-117) |
tickets | adb-tickets.tickets.type (JVM) and .ticketType (string or CatalogValue the CF port stored from the request DTO) | TICKET_TYPE.INCIDENT → TICKET_TYPE.DAMAGE; value INCIDENT → DAMAGE; a stored label is dropped | TicketTypeMap (tickets.service.ts:26-33) and the documents path (documents.service.ts:376-387) know DAMAGE only |
ticket-delegations | adb-tickets.delegations.delegatedScopes | tickets.incidents.<verb> → tickets.damages.<verb> | derived from the ticket type (DelegationCreationServiceImpl.java:74, :119-146) |
file-metadata | adb-files.files.fileType | FILE_TYPE.TICKET_INCIDENT → FILE_TYPE.TICKET_DAMAGE | the type 0.69 gives new ticket documents (create-event-modal.component.ts:327); the documents filter matches on it |
views-parts | adb-views.parts.vacancyTicketType | TICKET_TYPE.INCIDENT → TICKET_TYPE.DAMAGE | projection written by adb-views (TicketServiceImpl.java:175-183); the CDC route for tickets is insert-only (services/event-dispatch/src/routing.mjs:554-568), so it never refreshes by itself |
The delegation rename grants nothing new. Every role holds the same verbs on tickets.incidents and tickets.damages (packages/auth/scopes-by-role.generated.mjs, pinned by a test), and the rename is verb for verb. Delegated scopes are not read by any access check today (DelegationService.hasAccess / getEffectiveScopes have no caller); the rename keeps them true to what DelegationCreationServiceImpl would write for a DAMAGE ticket.
What is reported, never written: a ticket whose ticketType contradicts itself (id WORK, value INCIDENT, …) — never half-migrated; a configuration row whose key (nature, event type, or target type + document nature) already exists under the new code — rewriting it would duplicate the entry in the 0.69 lists; a delegation with incident scopes on a resource that is not a ticket. A document that matches but holds the code in a shape no plan rewrites (an array) is named as UNCHANGED.
What it deliberately leaves
| Place | Why |
|---|---|
adb-utilities.serials (easyId counters) | INCIDENT and DAMAGE share the TICKET bucket: TicketServiceImpl.java:1040, services/tickets/src/service.mjs:83-91 (pinned by a test) |
adb-tickets.events | events carry the EVENT type and the ticket id, never the ticket type (EventEntity.java, EventMapper.java:32-36) |
tickets.details.* | INCIDENT and DAMAGE map to the same TicketDetails (TicketMapper.java:157-168); no amount is touched |
ticketNature = TICKET_NATURE.INCIDENT | a nature, not the type; it stays offered under DAMAGE once ticket-configurations moved its row (probe) |
createAction / latestAction / ActionLogs paths (/tickets/incidents/…) | audit trail of what was called |
adb-utilities.catalog | reference values kept for history, the rollback and the JVM labels (probe) |
| aggregates, Atlas Search | computed at read time / synced by Atlas |
adb-persons.delegations | never derived from a ticket type (probe) |
Side effects of the writes
Every write is a $set, so each emits exactly one change event of type update (a test asserts no replace is ever emitted, and nothing but the code-bearing fields can change).
- Production (JVM, EventBridge). The live Realm app
triggers-kkeqzfiresticket-modifiedandfile-metadata-modifiedon insert, update, replace AND delete (infra/src/catalog/services.js:46-74, captured 2026-05-03). Their consumers write nothing for these updates:- adb-views
ticketModifiedregistersonInsertonly (MessagingFunctions.java:139-150): an update goes to the default handler, which logs a warning and acks (OperationTypeBasedCallbackProvider.java:35-39). Expect oneNo specific handler for actionTypewarning per migrated ticket in the adb-views logs. - adb-views
fileMetaDataModified(MessagingFunctions.java:224-238) writes only when the file nature maps to a diagnostic quantity of a part (PartsServiceImpl.java:637-663); adb-contracts (FileMetadataFunction.java:68-71) re-indexes on diagnostic files only. A ticket document is neither. - The trigger JSONs under
adb-infrastructure/mongodb-atlas/realm-cli/triggers/are the development app (service_name: adb-development), whereticket-updatedandfile-metadata-updatedlisten toREPLACEonly.
- adb-views
- Cloudflare (event-dispatch doorbells). Tickets and their events are routed on
insertonly (services/event-dispatch/src/routing.mjs:554-581); file metadata feeds the CF ports of the same two diagnostics-only consumers (services/views/src/consumers/handlers.mjs:207,services/contracts/src/consumer.mjs:1043-1160). - Configurations, delegations,
adb-views.parts— no trigger, no doorbell, no cache: the CF utilities cache holds/utilities/catalogand/utilities/i18nonly (services/utilities/src/cache.mjs:48), the JVM utilities caches persons and organisations only.
Prerequisites
- Greg has read a dry-run of the target cluster (his condition for any data write).
FILE_TYPE.TICKET_DAMAGEhas a catalog label. No catalog has it today (packages/i18n/locales/catalog.*.json, the JVMCATALOG_*.properties,adb-utilities/*-values-input.tsvonmain). Without it the migrated document types render as the raw id. 0.69 needs it anyway: it stamps that type on every new damage document. The existingFILE_TYPE.TICKET_INCIDENTlabel (fr « Sinistre », en « Damage ») is the one to copy.- A mongodump of the five collections, taken right before
--apply(below). pnpm installat the repository root (the script needs themongodbdriver of@adb/scripts).- The connection string in
MONGO_URI, exported in a shell that is not logged.--urirefuses credentials, so nothing secret reachespsor the shell history.
Steps — staging
1. Dry run (the default)
export MONGO_URI='mongodb+srv://…@adb-stage.cm0co.mongodb.net/'
node tools/scripts/migrate-incident-damage.mjs | tee stage-dry-run.txt
node tools/scripts/migrate-incident-damage.mjs --verbose # one line per document
The header names the cluster with the credentials stripped — read it:
direction: forward mode: dry-run
mongo: mongodb+srv://adb-stage.cm0co.mongodb.net (non-production)
Per family it prints the documents already carrying the new code, the candidates, what it would rewrite (per field), and every REPORT with its id and reason; then the read-only probes. Send stage-dry-run.txt to Greg.
2. Back up what will be written
for target in adb-tickets/tickets adb-tickets/delegations adb-files/files adb-utilities/configurations adb-views/parts; do
mongodump --uri "$MONGO_URI" --db "${target%/*}" --collection "${target#*/}" --out "backup-$(date +%Y%m%d)"
done
If the SRV record does not resolve from your network, use the standard (seed-list) connection string.
3. Apply
Start with one document per family, look at the result, then run the rest into the same log:
node tools/scripts/migrate-incident-damage.mjs --apply --limit 1 --log stage-incident-damage.jsonl
node tools/scripts/migrate-incident-damage.mjs --apply --log stage-incident-damage.jsonl
Per document: the intent (before + after) is appended to the log, then the guarded $set, then its result. A document changed since it was read is skipped (SKIPPED … changed since it was read) and logged as such. Interrupt with Ctrl-C and re-run the same command: done documents no longer match.
4. Verify
node tools/scripts/migrate-incident-damage.mjs # every family: to rewrite 0 (only REPORTs remain)
Then on staging: open a former incident in 0.69 / the React app (damage card visible, Document.s tab loads, « Sinistres » list shows it), and check the stage front still lists it — 0.65 asked both types (tickets.service.ts:193-201 at cf31ace71), 0.68 is not in this repository. Keep the log file with the backup.
Steps — production, at the 0.69 roll-out
Same steps, against the production connection string, with the flag — which the script requires for any cluster it cannot recognise as non-production:
node tools/scripts/migrate-incident-damage.mjs --i-know-this-is-prod | tee prod-dry-run.txt # Greg reviews
# mongodump as in step 2
node tools/scripts/migrate-incident-damage.mjs --i-know-this-is-prod --apply --limit 1 --log prod-incident-damage.jsonl
node tools/scripts/migrate-incident-damage.mjs --i-know-this-is-prod --apply --log prod-incident-damage.jsonl
node tools/scripts/migrate-incident-damage.mjs --i-know-this-is-prod # verify
While it runs, adb-views logs one No specific handler for actionType warning per migrated ticket (§ Side effects): expected, nothing is written. Run it with the 0.69 deploy. An older front is not hurt by running it a little earlier (0.65 listed DAMAGE and INCIDENT together). Re-run the dry-run a few days after the roll-out: 0.69 still stamps TICKET_TYPE.INCIDENT on a ticket created without a type (tickets.mapper.ts:488-489), and the script picks such tickets up again.
Rollback
node tools/scripts/migrate-incident-damage.mjs --rollback stage-incident-damage.jsonl # what it would restore
node tools/scripts/migrate-incident-damage.mjs --rollback stage-incident-damage.jsonl --apply
Newest entry first, each restore is a $set of the before-values guarded by the after-values: a document changed since the migration is left alone (not-in-migrated-state), an entry whose own write did not land is skipped, and an entry without a result line (the run died after logging its intent) is attempted and counted as uncertain. A second rollback restores nothing. The rollback refuses a log recorded against another cluster, and any entry this tool could not have written (the after-value must be what the family's own plan derives from the before-value), so a hand-edited log cannot write anything else. The same production guard applies.
Open questions
- Catalog label for
FILE_TYPE.TICKET_DAMAGE— needed by 0.69 regardless of this migration; to be added to the i18n sheet (and to the Mongoadb-utilities.catalogif a screen listsFILE_TYPEfrom it). - 0.69 defaults a type-less ticket to INCIDENT (
tickets.mapper.ts:488-489). Until that default is DAMAGE, INCIDENT tickets keep appearing; the script is the stopgap. - CF ticket creation stores the request DTO:
POST /ticketsspreads the body, so a ticket created through the CF service hastype: "Ticket"and its type id inticketType(services/tickets/src/controllers/tickets.mjs:350-363,ticketTypeIdOf:720-724). The script rewrites both shapes, but such tickets are invisible totype-filtered lists and get no easyId — a services/tickets defect, not this migration's. - The production cluster name is not in this repository, so the guard cannot recognise it — by construction it treats it as production and asks for the flag.