Handle support or engineering requests to delete metadata sections and/or metadata fields in global-metadata-enabled workspaces. Use when an incident asks to remove KeyPointerSection rows, soft-delete associated KeyPointer rows, verify ContractKeyPointer blast radius, confirm questionnaire/workflow linkage via template_field_name (draft + frozen), or choose Django admin / managerie versus a reviewed shell script. Trigger phrases: delete metadata section, delete metadata fields from global metadata workspace, delete key pointers under a section, delete_key_pointer_sections, global metadata cleanup, unlink template_field_name, used in active workflow, Metabase verify is_deleted, informed iq metadata fields.
Use this skill for operational cleanup requests in global-metadata-enabled workspaces where support or engineering needs to delete:
KeyPointerSection)KeyPointer)This is a staff-only / engineering workflow. It is not an end-user troubleshooting flow.
The important distinction is:
ContractKeyPointer rows still use ittemplate_field_name or deleting fields, confirm whether variables still appear on questionnaires (and optionally scan for intake references)core_condition| Situation | Use this skill | Action shape |
|---|---|---|
| Customer asks to delete a metadata section in a global-metadata-enabled workspace | Yes | Verify whether they want only the section hidden, or full cleanup including fields |
| Section is already deleted but fields under it still exist in DB | Yes | This is an expected incident pattern; decide whether separate field cleanup is needed |
| Customer asks to delete standalone metadata fields not tied to the section request | Yes | Treat as separate KeyPointer cleanup after CKP verification |
| Need to confirm KPs are not used in workflows before delete/unlink | Yes | Run questionnaire→workflow joins (draft + frozen); optional core_condition scan |
| Non-global-metadata workspace | Maybe not | Confirm behavior in that workspace first; this skill is optimized for global-metadata incidents |
Fields are still referenced by contracts (ContractKeyPointer rows exist) | Yes, for triage only | Do not blindly delete; escalate to an explicit migration / data-fix plan |
| Issue is about dropdown option mismatch, questionnaire validation, or metadata values being wrong | No | Use questionnaire-dropdown-global-metadata-mismatch |
| Customer / label | Slack | Rootly | Notes |
|---|---|---|---|
| Reserv | C0AGK1Z547M | 2903 | Feb 2026 — section vs field cleanup split |
| Glytec | C0APNNM0RK3 | 3057 | Mar 2026 — same operational pattern |
| Informed IQ (JW_ metadata) | C0AQ8TH1RN2 | 3069 | Apr 2026 — Jira SPD-43192; 21 KPs, section OLD_JW_SOW_Info (id 13722), WS 673430, US; 0 CKP; delete_key_pointer_sections dry-run + prod; reviewed shell soft-delete for KPs; 0 workflows on draft questionnaire join before execution |
Informed IQ incident summary (evidence-backed):
ContractKeyPointer rows for KP ids — safe to soft-delete definitions (still get explicit approval for prod).historic_contracts_keypointer → questionnaire_v2_questionvariable → questionnaire_v2_questionnairetoownerentitymapping (entity_type = 'WORKFLOW') → workflow_v1_workflow returned no rows for those KP ids (draft path). Re-run logic for frozen path if you need published-snapshot certainty.delete_key_pointer_sections with dry run first (managementcommandlog id 29344), then actual run (29345) on api.us.spotdraft.com.KeyPointer.is_deleted = True (separate Metabase proof of CKP=0 was recorded before prod run).Repeated operational conclusion:
KeyPointer rows.is_deleted, CKP count, and workflow linkage before declaring completion.| Object | Meaning |
|---|---|
KeyPointerSection | Metadata section shown in metadata manager / UI |
KeyPointer | Metadata field definition under a section or standalone |
ContractKeyPointer | Per-contract stored value for a key pointer |
is_deleted | Soft-delete flag; this is usually what “deletion” means in these incidents |
template_field_name / field_name | Variable linkage used by questionnaire / metadata resolution paths |
QuestionnaireToOwnerEntityMapping | entity_type = 'WORKFLOW' links a draft questionnaire to a workflow |
FrozenQuestionnaireToConsumerEntityMapping | entity_type = 'FROZEN_WORKFLOW' links frozen questionnaire to a frozen workflow snapshot |
When GLOBAL_METADATA is enabled for a workspace:
key_pointers_service.delete_key_pointers returns without deleting key pointers — see key_pointers/services/key_pointers_service.py.delete_key_pointer_sections still calls section soft-delete after the bulk KP use case; KP rows may remain if you relied on that command alone for “full cleanup.”Capture these before taking action:
{workspace_id} and cluster (us, in, eu, mea)template_field_name listIf the incident starts with labels only, derive IDs first from admin or Metabase before discussing deletion strategy.
Separate the ask into:
Do not merge them mentally. Example (Informed IQ): one section + 21 KPs tied to it.
https://api.{cluster}.spotdraft.com/admin/historic_contracts/keypointersection/{section_id}/change/https://api.{cluster}.spotdraft.com/admin/managerie/contracts_v3/delete_key_pointer_sections/KeyPointerSection.is_deleted and capture managementcommandlog links for dry-run vs actual.If the section is already deleted, do not assume the associated fields were deleted too.
SELECT
id,
label,
template_field_name,
field_name,
section_id,
is_deleted
FROM `spotdraft-prod.{dataset}.public_historic_contracts_keypointer`
WHERE created_by_workspace_id = {workspace_id}
AND section_id IN ({section_ids})
ORDER BY id;
Use prod_usa_db, prod_india_db, prod_eu_db, or prod_mea_db for {dataset}.
SELECT
id,
contract_id,
key_pointer_id,
value
FROM `spotdraft-prod.{dataset}.public_contracts_v3_contractkeypointer`
WHERE created_by_workspace_id = {workspace_id}
AND key_pointer_id IN ({key_pointer_ids})
ORDER BY key_pointer_id, contract_id;
Routing rule:
Postgres / Metabase pattern (matches incident 3069 draft check — adjust schema if your warehouse prefixes tables):
Join chain:
historic_contracts_keypointer.template_field_name = questionnaire_v2_questionvariable.name
→ questionnaire_v2_questionnairetoownerentitymapping.questionnaire_id
→ filter entity_type = 'WORKFLOW'
→ workflow_v1_workflow.id = entity_id
Required filters:
kp.created_by_workspace_id = {workspace_id} AND kp.is_deleted = false AND kp.template_field_name IS NOT NULLqv.created_by_workspace_id = {workspace_id} AND qv.is_deleted = falseqtom.created_by_workspace_id = {workspace_id} AND qtom.is_deleted = falsew.tenant_workspace_id = {workspace_id} AND w.is_deleted = falsew.status = 'PUBLISHED'Empty result ⇒ no draft workflow-owned questionnaire variable with that name for those KPs (in that workspace).
Repeat using:
questionnaire_v2_frozenquestionvariable (name, frozen_questionnaire_id)questionnaire_v2_frozenquestionnairetoconsumerentitymapping with entity_type = 'FROZEN_WORKFLOW'workflow_v1_frozenworkflow → workflow_v1_workflowSame workspace and is_deleted discipline on each hop.
If stakeholders ask about conditions (not just questionnaire variables), scan core_condition for the workspace where entity_type IN ('WORKFLOW','FROZEN_WORKFLOW') and condition blobs reference template_field_name / intake.{name} (JSON logic, AST, or complex expression text).
Bulk deletion can reject:
pointers_configRelevant code:
contracts_v3/management/commands/delete_key_pointers.pycontracts_v3/management/commands/delete_key_pointer_sections.pycontracts_v3/contract_key_pointers/domain/use_cases/delete_bulk_key_pointers_use_case.pycontracts_v3/contract_key_pointers/domain/use_cases/delete_bulk_key_pointer_sections_use_case.pyIf blockers fire, escalate instead of forcing raw DB edits.
KeyPointerSection.is_deleted = truedelete_key_pointer_sections — always dry-run first, then production, and link managementcommandlog in the incident thread.After CKP = 0 (or approved plan) and workflow checks:
QuerySet.update(is_deleted=True) in a reviewed shell with dry_run over per-row save() in loops.scripts/oncall_mitigations/glytec_req_to_delete_metadata_section.py as the shape of the incident-3069 run; prove CKP = 0 in Metabase (or assert in shell) before mutating prod, and get peer review regardless of filename.is_deleted = true (Metabase / admin)is_deleted = true for target idsspotdraft-prod.prod_usa_dbspotdraft-prod.prod_india_dbspotdraft-prod.prod_eu_dbspotdraft-prod.prod_mea_dbProd tables usually carry the public_ prefix.
| Item | Ref |
|---|---|
| Reserv deleting metadata section | Slack C0AGK1Z547M, Rootly 2903 |
| Glytec deleting metadata fields and sections | Slack C0APNNM0RK3, Rootly 3057 |
| Informed IQ — OLD_JW_SOW_Info + 21 KPs | Slack C0AQ8TH1RN2, Rootly 3069 |