[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F74TZ6BKY34JGF09CD6FAR":3,"skill-same:01M6F74TZ6BKY34JGF09CD6FAR":78},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":46,"linkOnly":47,"markdownContent":48,"markdownBody":49,"frontMatter":50,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":6,"ownerHtmlUrl":60,"ownerBlogUrl":61,"ownerDescription":62,"updatedAt":63,"install":64,"resources":77},"01M6F74TZ6BKY34JGF09CD6FAR","guidewire-data-handling",null,"Data handling best practices for Guidewire InsuranceSuite including entity management,\ndata migration, batch operations, and data governance.\nTrigger with phrases like \"guidewire data\", \"entity management\",\n\"data migration\", \"batch processing\", \"data governance guidewire\".","Dicklesworthstone\u002Fpi_agent_rust","Dicklesworthstone","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F35050222?v=4","01M6CG26AR942CSVMN1M5E3DSE","Jeff Emanuel","01M6CG26EPP71PSHW1VDS71121",1991,"MIT","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fguidewire-pack\u002Fskills\u002Fguidewire-data-handling\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],"guidewire data handling","data","management","for","entity management","with","processing","trigger","io","gradle","migration","operations","governance","edit","write","read","curl","grep","Bash","batch","data governance","best practices","2026-11-10 10:12:30",false,"---\nname: guidewire-data-handling\ndescription: |\n  Data handling best practices for Guidewire InsuranceSuite including entity management,\n  data migration, batch operations, and data governance.\n  Trigger with phrases like \"guidewire data\", \"entity management\",\n  \"data migration\", \"batch processing\", \"data governance guidewire\".\nallowed-tools: Read, Write, Edit, Bash(curl:*), Bash(gradle:*), Grep\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\n---\n\n# Guidewire Data Handling\n\n## Overview\n\nBest practices for data handling in Guidewire InsuranceSuite including entity management, CRUD operations, data migration, batch processing, and data governance.\n\n## Prerequisites\n\n- Understanding of Guidewire data model\n- Access to Guidewire documentation\n- Familiarity with Gosu programming\n\n## Data Model Overview\n\n```\n┌─────────────────────────────────────────────────────────────────────────────────┐\n│                        Guidewire Core Data Model                                 │\n├─────────────────────────────────────────────────────────────────────────────────┤\n│                                                                                  │\n│  ┌───────────────────────────────────────────────────────────────────────────┐  │\n│  │                            PolicyCenter                                    │  │\n│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐               │  │\n│  │  │ Account │───▶│  Policy │───▶│ Period  │───▶│Coverage │               │  │\n│  │  │         │    │         │    │         │    │         │               │  │\n│  │  │Contacts │    │ Jobs    │    │ Lines   │    │ Terms   │               │  │\n│  │  └─────────┘    └─────────┘    └─────────┘    └─────────┘               │  │\n│  └───────────────────────────────────────────────────────────────────────────┘  │\n│                                                                                  │\n│  ┌───────────────────────────────────────────────────────────────────────────┐  │\n│  │                            ClaimCenter                                     │  │\n│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐               │  │\n│  │  │  Claim  │───▶│Exposure │───▶│ Reserve │───▶│ Payment │               │  │\n│  │  │         │    │         │    │         │    │         │               │  │\n│  │  │Incidents│    │Claimants│    │ Checks  │    │Recovery │               │  │\n│  │  └─────────┘    └─────────┘    └─────────┘    └─────────┘               │  │\n│  └───────────────────────────────────────────────────────────────────────────┘  │\n│                                                                                  │\n│  ┌───────────────────────────────────────────────────────────────────────────┐  │\n│  │                           BillingCenter                                    │  │\n│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐               │  │\n│  │  │ Account │───▶│ Invoice │───▶│ Charge  │───▶│ Payment │               │  │\n│  │  │         │    │         │    │         │    │         │               │  │\n│  │  │ Policy  │    │  Items  │    │ Credits │    │Disbursement│            │  │\n│  │  └─────────┘    └─────────┘    └─────────┘    └─────────┘               │  │\n│  └───────────────────────────────────────────────────────────────────────────┘  │\n│                                                                                  │\n└─────────────────────────────────────────────────────────────────────────────────┘\n```\n\n## Instructions\n\n### Step 1: Entity CRUD Operations\n\n```gosu\n\u002F\u002F Gosu entity management patterns\npackage gw.data.entity\n\nuses gw.api.database.Query\nuses gw.api.util.Logger\nuses gw.transaction.Transaction\n\nclass EntityManager {\n  private static final var LOG = Logger.forCategory(\"EntityManager\")\n\n  \u002F\u002F CREATE - Single entity\n  static function createAccount(data : AccountData) : Account {\n    return Transaction.runWithNewBundle(\\bundle -> {\n      var account = new Account(bundle)\n      account.AccountNumber = generateAccountNumber()\n\n      var contact = new Person(bundle)\n      contact.FirstName = data.FirstName\n      contact.LastName = data.LastName\n      contact.DateOfBirth = data.DateOfBirth\n      contact.EmailAddress1 = data.Email\n\n      var address = new Address(bundle)\n      address.AddressLine1 = data.AddressLine1\n      address.City = data.City\n      address.State = State.get(data.StateCode)\n      address.PostalCode = data.PostalCode\n      contact.PrimaryAddress = address\n\n      account.AccountHolderContact = contact\n\n      LOG.info(\"Created account: ${account.AccountNumber}\")\n      return account\n    })\n  }\n\n  \u002F\u002F READ - Single entity\n  static function findAccountByNumber(accountNumber : String) : Account {\n    return Query.make(Account)\n      .compare(Account#AccountNumber, Equals, accountNumber)\n      .select()\n      .AtMostOneRow\n  }\n\n  \u002F\u002F READ - Multiple entities with filtering\n  static function findActiveAccountsByState(stateCode : String, limit : int = 100) : List\u003CAccount> {\n    return Query.make(Account)\n      .compare(Account#AccountStatus, Equals, AccountStatus.TC_ACTIVE)\n      .join(Account#AccountHolderContact)\n      .join(Contact#PrimaryAddress)\n      .compare(Address#State, Equals, State.get(stateCode))\n      .select()\n      .orderBy(\\a -> a.AccountNumber)\n      .take(limit)\n      .toList()\n  }\n\n  \u002F\u002F UPDATE - Single entity\n  static function updateAccount(accountNumber : String, data : AccountUpdateData) : Account {\n    return Transaction.runWithNewBundle(\\bundle -> {\n      var account = findAccountByNumber(accountNumber)\n      if (account == null) {\n        throw new IllegalArgumentException(\"Account not found: ${accountNumber}\")\n      }\n\n      account = bundle.add(account)\n\n      if (data.Email != null) {\n        account.AccountHolderContact.EmailAddress1 = data.Email\n      }\n\n      if (data.Phone != null) {\n        account.AccountHolderContact.HomePhone = data.Phone\n      }\n\n      LOG.info(\"Updated account: ${accountNumber}\")\n      return account\n    })\n  }\n\n  \u002F\u002F DELETE - Single entity (soft delete)\n  static function deactivateAccount(accountNumber : String) {\n    Transaction.runWithNewBundle(\\bundle -> {\n      var account = findAccountByNumber(accountNumber)\n      if (account == null) {\n        throw new IllegalArgumentException(\"Account not found: ${accountNumber}\")\n      }\n\n      account = bundle.add(account)\n      account.AccountStatus = AccountStatus.TC_INACTIVE\n\n      LOG.info(\"Deactivated account: ${accountNumber}\")\n    })\n  }\n\n  private static function generateAccountNumber() : String {\n    return \"ACC-${System.currentTimeMillis()}\"\n  }\n}\n```\n\n### Step 2: Batch Data Operations\n\n```gosu\n\u002F\u002F Batch processing patterns\npackage gw.data.batch\n\nuses gw.api.database.Query\nuses gw.api.util.Logger\nuses gw.transaction.Transaction\nuses java.util.concurrent.atomic.AtomicInteger\n\nclass BatchProcessor {\n  private static final var LOG = Logger.forCategory(\"BatchProcessor\")\n  private static final var DEFAULT_BATCH_SIZE = 100\n\n  \u002F\u002F Process large datasets in batches\n  static function processPolicies(processor(policy : Policy)) : BatchResult {\n    var result = new BatchResult()\n    var batchCount = new AtomicInteger(0)\n\n    var query = Query.make(Policy)\n      .compare(Policy#Status, Equals, PolicyStatus.TC_INFORCE)\n\n    var iterator = query.select().iterator()\n    var batch = new ArrayList\u003CPolicy>()\n\n    while (iterator.hasNext()) {\n      batch.add(iterator.next())\n\n      if (batch.size() >= DEFAULT_BATCH_SIZE) {\n        processBatch(batch, processor, result)\n        batch.clear()\n        batchCount.incrementAndGet()\n        LOG.info(\"Processed batch ${batchCount.get()}\")\n      }\n    }\n\n    \u002F\u002F Process remaining\n    if (!batch.Empty) {\n      processBatch(batch, processor, result)\n    }\n\n    LOG.info(\"Batch processing complete: ${result.Processed} processed, ${result.Failed} failed\")\n    return result\n  }\n\n  private static function processBatch(\n    batch : List\u003CPolicy>,\n    processor(policy : Policy),\n    result : BatchResult\n  ) {\n    Transaction.runWithNewBundle(\\bundle -> {\n      batch.each(\\policy -> {\n        try {\n          var p = bundle.add(policy)\n          processor(p)\n          result.incrementProcessed()\n        } catch (e : Exception) {\n          LOG.error(\"Failed to process policy ${policy.PolicyNumber}\", e)\n          result.addError(policy.PolicyNumber, e.Message)\n          result.incrementFailed()\n        }\n      })\n    })\n  }\n\n  \u002F\u002F Parallel batch processing\n  static function processParallel\u003CT>(\n    items : List\u003CT>,\n    processor(item : T),\n    threadCount : int = 4\n  ) : BatchResult {\n    var result = new BatchResult()\n    var executor = java.util.concurrent.Executors.newFixedThreadPool(threadCount)\n\n    try {\n      var futures = items.partition(DEFAULT_BATCH_SIZE).map(\\batch -> {\n        executor.submit(\\-> {\n          Transaction.runWithNewBundle(\\bundle -> {\n            batch.each(\\item -> {\n              try {\n                processor(item)\n                result.incrementProcessed()\n              } catch (e : Exception) {\n                LOG.error(\"Processing failed\", e)\n                result.incrementFailed()\n              }\n            })\n          })\n        })\n      })\n\n      \u002F\u002F Wait for completion\n      futures.each(\\f -> f.get())\n    } finally {\n      executor.shutdown()\n    }\n\n    return result\n  }\n}\n\nclass BatchResult {\n  private var _processed : AtomicInteger = new AtomicInteger(0)\n  private var _failed : AtomicInteger = new AtomicInteger(0)\n  private var _errors = new java.util.concurrent.ConcurrentHashMap\u003CString, String>()\n\n  property get Processed() : int { return _processed.get() }\n  property get Failed() : int { return _failed.get() }\n  property get Errors() : Map\u003CString, String> { return _errors }\n\n  function incrementProcessed() { _processed.incrementAndGet() }\n  function incrementFailed() { _failed.incrementAndGet() }\n  function addError(key : String, message : String) { _errors.put(key, message) }\n}\n```\n\n### Step 3: Data Migration\n\n```typescript\n\u002F\u002F API-based data migration\ninterface MigrationConfig {\n  sourceSystem: string;\n  targetEnvironment: string;\n  batchSize: number;\n  dryRun: boolean;\n}\n\ninterface MigrationResult {\n  totalRecords: number;\n  successCount: number;\n  errorCount: number;\n  errors: Array\u003C{ id: string; error: string }>;\n  duration: number;\n}\n\nclass DataMigration {\n  private config: MigrationConfig;\n  private guidewireClient: GuidewireClient;\n\n  constructor(config: MigrationConfig) {\n    this.config = config;\n    this.guidewireClient = new GuidewireClient(config.targetEnvironment);\n  }\n\n  async migrateAccounts(sourceAccounts: SourceAccount[]): Promise\u003CMigrationResult> {\n    const startTime = Date.now();\n    const result: MigrationResult = {\n      totalRecords: sourceAccounts.length,\n      successCount: 0,\n      errorCount: 0,\n      errors: [],\n      duration: 0\n    };\n\n    console.log(`Starting migration of ${sourceAccounts.length} accounts`);\n\n    \u002F\u002F Process in batches\n    for (let i = 0; i \u003C sourceAccounts.length; i += this.config.batchSize) {\n      const batch = sourceAccounts.slice(i, i + this.config.batchSize);\n      console.log(`Processing batch ${Math.floor(i \u002F this.config.batchSize) + 1}`);\n\n      await Promise.all(batch.map(async (sourceAccount) => {\n        try {\n          const gwAccount = this.transformAccount(sourceAccount);\n\n          if (!this.config.dryRun) {\n            await this.guidewireClient.createAccount(gwAccount);\n          }\n\n          result.successCount++;\n        } catch (error) {\n          result.errorCount++;\n          result.errors.push({\n            id: sourceAccount.id,\n            error: error instanceof Error ? error.message : 'Unknown error'\n          });\n        }\n      }));\n\n      \u002F\u002F Rate limiting pause between batches\n      await sleep(1000);\n    }\n\n    result.duration = Date.now() - startTime;\n    console.log(`Migration complete: ${result.successCount} succeeded, ${result.errorCount} failed`);\n\n    return result;\n  }\n\n  private transformAccount(source: SourceAccount): GuidewireAccount {\n    return {\n      accountHolder: {\n        contactSubtype: source.isCompany ? 'Company' : 'Person',\n        firstName: source.firstName,\n        lastName: source.lastName,\n        companyName: source.companyName,\n        primaryAddress: {\n          addressLine1: source.address.line1,\n          addressLine2: source.address.line2,\n          city: source.address.city,\n          state: { code: this.mapStateCode(source.address.state) },\n          postalCode: source.address.zip,\n          country: { code: 'US' }\n        },\n        emailAddress1: source.email,\n        homePhone: source.phone\n      },\n      producerCodes: [{ id: this.mapProducerCode(source.agentCode) }]\n    };\n  }\n\n  private mapStateCode(sourceState: string): string {\n    \u002F\u002F Map source system state codes to Guidewire codes\n    const stateMap: Record\u003Cstring, string> = {\n      'California': 'CA',\n      'CA': 'CA',\n      \u002F\u002F Add more mappings\n    };\n    return stateMap[sourceState] || sourceState;\n  }\n}\n```\n\n### Step 4: Data Validation\n\n```gosu\n\u002F\u002F Data validation framework\npackage gw.data.validation\n\nuses gw.api.util.Logger\n\nclass DataValidator {\n  private static final var LOG = Logger.forCategory(\"DataValidator\")\n\n  \u002F\u002F Validate account data\n  static function validateAccount(account : Account) : ValidationResult {\n    var result = new ValidationResult()\n\n    \u002F\u002F Required fields\n    if (account.AccountHolderContact == null) {\n      result.addError(\"AccountHolderContact\", \"Account holder contact is required\")\n    }\n\n    if (account.AccountNumber == null || account.AccountNumber.Empty) {\n      result.addError(\"AccountNumber\", \"Account number is required\")\n    }\n\n    \u002F\u002F Contact validation\n    if (account.AccountHolderContact != null) {\n      var contact = account.AccountHolderContact\n      if (contact typeis Person) {\n        if (contact.FirstName == null || contact.FirstName.Empty) {\n          result.addError(\"FirstName\", \"First name is required for person\")\n        }\n        if (contact.LastName == null || contact.LastName.Empty) {\n          result.addError(\"LastName\", \"Last name is required for person\")\n        }\n      } else if (contact typeis Company) {\n        if (contact.Name == null || contact.Name.Empty) {\n          result.addError(\"CompanyName\", \"Company name is required\")\n        }\n      }\n\n      \u002F\u002F Address validation\n      if (contact.PrimaryAddress != null) {\n        validateAddress(contact.PrimaryAddress, result)\n      } else {\n        result.addError(\"PrimaryAddress\", \"Primary address is required\")\n      }\n    }\n\n    return result\n  }\n\n  private static function validateAddress(address : Address, result : ValidationResult) {\n    if (address.AddressLine1 == null || address.AddressLine1.Empty) {\n      result.addError(\"AddressLine1\", \"Address line 1 is required\")\n    }\n    if (address.City == null || address.City.Empty) {\n      result.addError(\"City\", \"City is required\")\n    }\n    if (address.State == null) {\n      result.addError(\"State\", \"State is required\")\n    }\n    if (address.PostalCode == null || address.PostalCode.Empty) {\n      result.addError(\"PostalCode\", \"Postal code is required\")\n    }\n\n    \u002F\u002F Postal code format validation\n    if (address.PostalCode != null && !address.PostalCode.matches(\"^\\\\d{5}(-\\\\d{4})?$\")) {\n      result.addError(\"PostalCode\", \"Invalid postal code format\")\n    }\n  }\n\n  \u002F\u002F Validate policy data\n  static function validatePolicy(policy : Policy) : ValidationResult {\n    var result = new ValidationResult()\n\n    if (policy.Account == null) {\n      result.addError(\"Account\", \"Policy must be associated with an account\")\n    }\n\n    if (policy.EffectiveDate == null) {\n      result.addError(\"EffectiveDate\", \"Effective date is required\")\n    }\n\n    if (policy.ExpirationDate == null) {\n      result.addError(\"ExpirationDate\", \"Expiration date is required\")\n    }\n\n    if (policy.EffectiveDate != null && policy.ExpirationDate != null) {\n      if (policy.ExpirationDate.before(policy.EffectiveDate)) {\n        result.addError(\"ExpirationDate\", \"Expiration date must be after effective date\")\n      }\n    }\n\n    return result\n  }\n}\n\nclass ValidationResult {\n  private var _errors = new ArrayList\u003CValidationError>()\n\n  property get HasErrors() : boolean { return !_errors.Empty }\n  property get Errors() : List\u003CValidationError> { return _errors }\n\n  function addError(field : String, message : String) {\n    _errors.add(new ValidationError(field, message))\n  }\n\n  function throwIfInvalid() {\n    if (HasErrors) {\n      throw new ValidationException(\n        \"Validation failed: \" + _errors.map(\\e -> e.toString()).join(\", \")\n      )\n    }\n  }\n}\n```\n\n### Step 5: Data Export\n\n```gosu\n\u002F\u002F Data export utilities\npackage gw.data.export\n\nuses gw.api.database.Query\nuses java.io.FileWriter\nuses java.io.BufferedWriter\n\nclass DataExporter {\n\n  \u002F\u002F Export to CSV\n  static function exportAccountsToCSV(filename : String, filter : AccountFilter = null) {\n    var writer = new BufferedWriter(new FileWriter(filename))\n\n    try {\n      \u002F\u002F Write header\n      writer.write(\"AccountNumber,FirstName,LastName,Email,Phone,Address,City,State,Zip\\\\n\")\n\n      \u002F\u002F Query accounts\n      var query = Query.make(Account)\n      if (filter != null) {\n        if (filter.Status != null) {\n          query.compare(Account#AccountStatus, Equals, filter.Status)\n        }\n        if (filter.CreatedAfter != null) {\n          query.compare(Account#CreateTime, GreaterThan, filter.CreatedAfter)\n        }\n      }\n\n      \u002F\u002F Stream results to file\n      query.select().each(\\account -> {\n        var contact = account.AccountHolderContact\n        var address = contact.PrimaryAddress\n\n        writer.write(escapeCSV(account.AccountNumber) + \",\")\n        writer.write(escapeCSV(contact typeis Person ? contact.FirstName : \"\") + \",\")\n        writer.write(escapeCSV(contact typeis Person ? contact.LastName : contact.Name) + \",\")\n        writer.write(escapeCSV(contact.EmailAddress1 ?: \"\") + \",\")\n        writer.write(escapeCSV(contact.HomePhone ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.AddressLine1 ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.City ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.State?.Code ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.PostalCode ?: \"\") + \"\\\\n\")\n      })\n    } finally {\n      writer.close()\n    }\n  }\n\n  \u002F\u002F Export to JSON\n  static function exportAccountsToJSON(filename : String, limit : int = 1000) : int {\n    var accounts = Query.make(Account)\n      .compare(Account#AccountStatus, Equals, AccountStatus.TC_ACTIVE)\n      .select()\n      .take(limit)\n      .toList()\n\n    var json = accounts.map(\\a -> accountToMap(a))\n    var jsonString = gw.api.json.JsonObject.toJson(json)\n\n    var writer = new FileWriter(filename)\n    writer.write(jsonString)\n    writer.close()\n\n    return accounts.Count\n  }\n\n  private static function accountToMap(account : Account) : Map\u003CString, Object> {\n    var contact = account.AccountHolderContact\n    return {\n      \"accountNumber\" -> account.AccountNumber,\n      \"status\" -> account.AccountStatus.Code,\n      \"contact\" -> {\n        \"name\" -> contact.DisplayName,\n        \"email\" -> contact.EmailAddress1,\n        \"phone\" -> contact.HomePhone\n      },\n      \"createdDate\" -> account.CreateTime.toString()\n    }\n  }\n\n  private static function escapeCSV(value : String) : String {\n    if (value.contains(\",\") || value.contains(\"\\\"\") || value.contains(\"\\\\n\")) {\n      return \"\\\"\" + value.replace(\"\\\"\", \"\\\"\\\"\") + \"\\\"\"\n    }\n    return value\n  }\n}\n```\n\n### Step 6: Data Governance\n\n```typescript\n\u002F\u002F Data governance and PII handling\ninterface PIIField {\n  fieldPath: string;\n  classification: 'PII' | 'PHI' | 'PCI' | 'SENSITIVE';\n  maskingRule: 'full' | 'partial' | 'hash';\n}\n\nconst piiFields: PIIField[] = [\n  { fieldPath: 'contact.ssn', classification: 'PII', maskingRule: 'partial' },\n  { fieldPath: 'contact.dateOfBirth', classification: 'PII', maskingRule: 'partial' },\n  { fieldPath: 'contact.emailAddress1', classification: 'PII', maskingRule: 'partial' },\n  { fieldPath: 'bankAccount.accountNumber', classification: 'PCI', maskingRule: 'partial' },\n  { fieldPath: 'bankAccount.routingNumber', classification: 'PCI', maskingRule: 'full' }\n];\n\nclass PIIMasker {\n  mask(data: any, fields: PIIField[]): any {\n    const masked = JSON.parse(JSON.stringify(data));\n\n    fields.forEach(field => {\n      const value = this.getNestedValue(masked, field.fieldPath);\n      if (value) {\n        const maskedValue = this.applyMaskingRule(value, field.maskingRule);\n        this.setNestedValue(masked, field.fieldPath, maskedValue);\n      }\n    });\n\n    return masked;\n  }\n\n  private applyMaskingRule(value: string, rule: string): string {\n    switch (rule) {\n      case 'full':\n        return '*'.repeat(value.length);\n      case 'partial':\n        if (value.length \u003C= 4) return '****';\n        return '*'.repeat(value.length - 4) + value.slice(-4);\n      case 'hash':\n        return this.hashValue(value);\n      default:\n        return '****';\n    }\n  }\n\n  private hashValue(value: string): string {\n    \u002F\u002F SHA-256 hash for one-way masking\n    const crypto = require('crypto');\n    return crypto.createHash('sha256').update(value).digest('hex').substring(0, 16);\n  }\n}\n\n\u002F\u002F Data retention policy\nclass DataRetentionPolicy {\n  private retentionRules: Map\u003Cstring, number> = new Map([\n    ['claim', 7 * 365],        \u002F\u002F 7 years\n    ['policy', 10 * 365],      \u002F\u002F 10 years\n    ['auditLog', 90],          \u002F\u002F 90 days\n    ['sessionData', 1],        \u002F\u002F 1 day\n  ]);\n\n  async enforceRetention(): Promise\u003CRetentionResult> {\n    const result: RetentionResult = { deleted: {}, errors: [] };\n\n    for (const [entityType, retentionDays] of this.retentionRules) {\n      try {\n        const cutoffDate = new Date();\n        cutoffDate.setDate(cutoffDate.getDate() - retentionDays);\n\n        const deletedCount = await this.deleteOldRecords(entityType, cutoffDate);\n        result.deleted[entityType] = deletedCount;\n      } catch (error) {\n        result.errors.push({ entityType, error: error.message });\n      }\n    }\n\n    return result;\n  }\n}\n```\n\n## Data Handling Best Practices\n\n| Practice | Description |\n|----------|-------------|\n| Use Bundles | Always use Transaction.runWithNewBundle for writes |\n| Batch Processing | Process large datasets in batches of 100-500 |\n| Lazy Loading | Use select() and iterate, don't toList() large results |\n| Index Awareness | Query on indexed fields for performance |\n| Validation First | Validate data before database operations |\n| Audit Trail | Log all data modifications |\n| PII Protection | Mask or encrypt sensitive data |\n\n## Output\n\n- Entity CRUD operations\n- Batch processing framework\n- Data migration utilities\n- Validation framework\n- Export capabilities\n- Data governance tools\n\n## Resources\n\n- [Guidewire Data Model](https:\u002F\u002Fdocs.guidewire.com\u002Fcloud\u002F)\n- [Query API Documentation](https:\u002F\u002Fdocs.guidewire.com\u002Fcloud\u002Fpc\u002F202503\u002Fapiref\u002F)\n\n## Next Steps\n\nFor role-based access control, see `guidewire-enterprise-rbac`.\n","# Guidewire Data Handling\n\n## Overview\n\nBest practices for data handling in Guidewire InsuranceSuite including entity management, CRUD operations, data migration, batch processing, and data governance.\n\n## Prerequisites\n\n- Understanding of Guidewire data model\n- Access to Guidewire documentation\n- Familiarity with Gosu programming\n\n## Data Model Overview\n\n```\n┌─────────────────────────────────────────────────────────────────────────────────┐\n│                        Guidewire Core Data Model                                 │\n├─────────────────────────────────────────────────────────────────────────────────┤\n│                                                                                  │\n│  ┌───────────────────────────────────────────────────────────────────────────┐  │\n│  │                            PolicyCenter                                    │  │\n│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐               │  │\n│  │  │ Account │───▶│  Policy │───▶│ Period  │───▶│Coverage │               │  │\n│  │  │         │    │         │    │         │    │         │               │  │\n│  │  │Contacts │    │ Jobs    │    │ Lines   │    │ Terms   │               │  │\n│  │  └─────────┘    └─────────┘    └─────────┘    └─────────┘               │  │\n│  └───────────────────────────────────────────────────────────────────────────┘  │\n│                                                                                  │\n│  ┌───────────────────────────────────────────────────────────────────────────┐  │\n│  │                            ClaimCenter                                     │  │\n│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐               │  │\n│  │  │  Claim  │───▶│Exposure │───▶│ Reserve │───▶│ Payment │               │  │\n│  │  │         │    │         │    │         │    │         │               │  │\n│  │  │Incidents│    │Claimants│    │ Checks  │    │Recovery │               │  │\n│  │  └─────────┘    └─────────┘    └─────────┘    └─────────┘               │  │\n│  └───────────────────────────────────────────────────────────────────────────┘  │\n│                                                                                  │\n│  ┌───────────────────────────────────────────────────────────────────────────┐  │\n│  │                           BillingCenter                                    │  │\n│  │  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐               │  │\n│  │  │ Account │───▶│ Invoice │───▶│ Charge  │───▶│ Payment │               │  │\n│  │  │         │    │         │    │         │    │         │               │  │\n│  │  │ Policy  │    │  Items  │    │ Credits │    │Disbursement│            │  │\n│  │  └─────────┘    └─────────┘    └─────────┘    └─────────┘               │  │\n│  └───────────────────────────────────────────────────────────────────────────┘  │\n│                                                                                  │\n└─────────────────────────────────────────────────────────────────────────────────┘\n```\n\n## Instructions\n\n### Step 1: Entity CRUD Operations\n\n```gosu\n\u002F\u002F Gosu entity management patterns\npackage gw.data.entity\n\nuses gw.api.database.Query\nuses gw.api.util.Logger\nuses gw.transaction.Transaction\n\nclass EntityManager {\n  private static final var LOG = Logger.forCategory(\"EntityManager\")\n\n  \u002F\u002F CREATE - Single entity\n  static function createAccount(data : AccountData) : Account {\n    return Transaction.runWithNewBundle(\\bundle -> {\n      var account = new Account(bundle)\n      account.AccountNumber = generateAccountNumber()\n\n      var contact = new Person(bundle)\n      contact.FirstName = data.FirstName\n      contact.LastName = data.LastName\n      contact.DateOfBirth = data.DateOfBirth\n      contact.EmailAddress1 = data.Email\n\n      var address = new Address(bundle)\n      address.AddressLine1 = data.AddressLine1\n      address.City = data.City\n      address.State = State.get(data.StateCode)\n      address.PostalCode = data.PostalCode\n      contact.PrimaryAddress = address\n\n      account.AccountHolderContact = contact\n\n      LOG.info(\"Created account: ${account.AccountNumber}\")\n      return account\n    })\n  }\n\n  \u002F\u002F READ - Single entity\n  static function findAccountByNumber(accountNumber : String) : Account {\n    return Query.make(Account)\n      .compare(Account#AccountNumber, Equals, accountNumber)\n      .select()\n      .AtMostOneRow\n  }\n\n  \u002F\u002F READ - Multiple entities with filtering\n  static function findActiveAccountsByState(stateCode : String, limit : int = 100) : List\u003CAccount> {\n    return Query.make(Account)\n      .compare(Account#AccountStatus, Equals, AccountStatus.TC_ACTIVE)\n      .join(Account#AccountHolderContact)\n      .join(Contact#PrimaryAddress)\n      .compare(Address#State, Equals, State.get(stateCode))\n      .select()\n      .orderBy(\\a -> a.AccountNumber)\n      .take(limit)\n      .toList()\n  }\n\n  \u002F\u002F UPDATE - Single entity\n  static function updateAccount(accountNumber : String, data : AccountUpdateData) : Account {\n    return Transaction.runWithNewBundle(\\bundle -> {\n      var account = findAccountByNumber(accountNumber)\n      if (account == null) {\n        throw new IllegalArgumentException(\"Account not found: ${accountNumber}\")\n      }\n\n      account = bundle.add(account)\n\n      if (data.Email != null) {\n        account.AccountHolderContact.EmailAddress1 = data.Email\n      }\n\n      if (data.Phone != null) {\n        account.AccountHolderContact.HomePhone = data.Phone\n      }\n\n      LOG.info(\"Updated account: ${accountNumber}\")\n      return account\n    })\n  }\n\n  \u002F\u002F DELETE - Single entity (soft delete)\n  static function deactivateAccount(accountNumber : String) {\n    Transaction.runWithNewBundle(\\bundle -> {\n      var account = findAccountByNumber(accountNumber)\n      if (account == null) {\n        throw new IllegalArgumentException(\"Account not found: ${accountNumber}\")\n      }\n\n      account = bundle.add(account)\n      account.AccountStatus = AccountStatus.TC_INACTIVE\n\n      LOG.info(\"Deactivated account: ${accountNumber}\")\n    })\n  }\n\n  private static function generateAccountNumber() : String {\n    return \"ACC-${System.currentTimeMillis()}\"\n  }\n}\n```\n\n### Step 2: Batch Data Operations\n\n```gosu\n\u002F\u002F Batch processing patterns\npackage gw.data.batch\n\nuses gw.api.database.Query\nuses gw.api.util.Logger\nuses gw.transaction.Transaction\nuses java.util.concurrent.atomic.AtomicInteger\n\nclass BatchProcessor {\n  private static final var LOG = Logger.forCategory(\"BatchProcessor\")\n  private static final var DEFAULT_BATCH_SIZE = 100\n\n  \u002F\u002F Process large datasets in batches\n  static function processPolicies(processor(policy : Policy)) : BatchResult {\n    var result = new BatchResult()\n    var batchCount = new AtomicInteger(0)\n\n    var query = Query.make(Policy)\n      .compare(Policy#Status, Equals, PolicyStatus.TC_INFORCE)\n\n    var iterator = query.select().iterator()\n    var batch = new ArrayList\u003CPolicy>()\n\n    while (iterator.hasNext()) {\n      batch.add(iterator.next())\n\n      if (batch.size() >= DEFAULT_BATCH_SIZE) {\n        processBatch(batch, processor, result)\n        batch.clear()\n        batchCount.incrementAndGet()\n        LOG.info(\"Processed batch ${batchCount.get()}\")\n      }\n    }\n\n    \u002F\u002F Process remaining\n    if (!batch.Empty) {\n      processBatch(batch, processor, result)\n    }\n\n    LOG.info(\"Batch processing complete: ${result.Processed} processed, ${result.Failed} failed\")\n    return result\n  }\n\n  private static function processBatch(\n    batch : List\u003CPolicy>,\n    processor(policy : Policy),\n    result : BatchResult\n  ) {\n    Transaction.runWithNewBundle(\\bundle -> {\n      batch.each(\\policy -> {\n        try {\n          var p = bundle.add(policy)\n          processor(p)\n          result.incrementProcessed()\n        } catch (e : Exception) {\n          LOG.error(\"Failed to process policy ${policy.PolicyNumber}\", e)\n          result.addError(policy.PolicyNumber, e.Message)\n          result.incrementFailed()\n        }\n      })\n    })\n  }\n\n  \u002F\u002F Parallel batch processing\n  static function processParallel\u003CT>(\n    items : List\u003CT>,\n    processor(item : T),\n    threadCount : int = 4\n  ) : BatchResult {\n    var result = new BatchResult()\n    var executor = java.util.concurrent.Executors.newFixedThreadPool(threadCount)\n\n    try {\n      var futures = items.partition(DEFAULT_BATCH_SIZE).map(\\batch -> {\n        executor.submit(\\-> {\n          Transaction.runWithNewBundle(\\bundle -> {\n            batch.each(\\item -> {\n              try {\n                processor(item)\n                result.incrementProcessed()\n              } catch (e : Exception) {\n                LOG.error(\"Processing failed\", e)\n                result.incrementFailed()\n              }\n            })\n          })\n        })\n      })\n\n      \u002F\u002F Wait for completion\n      futures.each(\\f -> f.get())\n    } finally {\n      executor.shutdown()\n    }\n\n    return result\n  }\n}\n\nclass BatchResult {\n  private var _processed : AtomicInteger = new AtomicInteger(0)\n  private var _failed : AtomicInteger = new AtomicInteger(0)\n  private var _errors = new java.util.concurrent.ConcurrentHashMap\u003CString, String>()\n\n  property get Processed() : int { return _processed.get() }\n  property get Failed() : int { return _failed.get() }\n  property get Errors() : Map\u003CString, String> { return _errors }\n\n  function incrementProcessed() { _processed.incrementAndGet() }\n  function incrementFailed() { _failed.incrementAndGet() }\n  function addError(key : String, message : String) { _errors.put(key, message) }\n}\n```\n\n### Step 3: Data Migration\n\n```typescript\n\u002F\u002F API-based data migration\ninterface MigrationConfig {\n  sourceSystem: string;\n  targetEnvironment: string;\n  batchSize: number;\n  dryRun: boolean;\n}\n\ninterface MigrationResult {\n  totalRecords: number;\n  successCount: number;\n  errorCount: number;\n  errors: Array\u003C{ id: string; error: string }>;\n  duration: number;\n}\n\nclass DataMigration {\n  private config: MigrationConfig;\n  private guidewireClient: GuidewireClient;\n\n  constructor(config: MigrationConfig) {\n    this.config = config;\n    this.guidewireClient = new GuidewireClient(config.targetEnvironment);\n  }\n\n  async migrateAccounts(sourceAccounts: SourceAccount[]): Promise\u003CMigrationResult> {\n    const startTime = Date.now();\n    const result: MigrationResult = {\n      totalRecords: sourceAccounts.length,\n      successCount: 0,\n      errorCount: 0,\n      errors: [],\n      duration: 0\n    };\n\n    console.log(`Starting migration of ${sourceAccounts.length} accounts`);\n\n    \u002F\u002F Process in batches\n    for (let i = 0; i \u003C sourceAccounts.length; i += this.config.batchSize) {\n      const batch = sourceAccounts.slice(i, i + this.config.batchSize);\n      console.log(`Processing batch ${Math.floor(i \u002F this.config.batchSize) + 1}`);\n\n      await Promise.all(batch.map(async (sourceAccount) => {\n        try {\n          const gwAccount = this.transformAccount(sourceAccount);\n\n          if (!this.config.dryRun) {\n            await this.guidewireClient.createAccount(gwAccount);\n          }\n\n          result.successCount++;\n        } catch (error) {\n          result.errorCount++;\n          result.errors.push({\n            id: sourceAccount.id,\n            error: error instanceof Error ? error.message : 'Unknown error'\n          });\n        }\n      }));\n\n      \u002F\u002F Rate limiting pause between batches\n      await sleep(1000);\n    }\n\n    result.duration = Date.now() - startTime;\n    console.log(`Migration complete: ${result.successCount} succeeded, ${result.errorCount} failed`);\n\n    return result;\n  }\n\n  private transformAccount(source: SourceAccount): GuidewireAccount {\n    return {\n      accountHolder: {\n        contactSubtype: source.isCompany ? 'Company' : 'Person',\n        firstName: source.firstName,\n        lastName: source.lastName,\n        companyName: source.companyName,\n        primaryAddress: {\n          addressLine1: source.address.line1,\n          addressLine2: source.address.line2,\n          city: source.address.city,\n          state: { code: this.mapStateCode(source.address.state) },\n          postalCode: source.address.zip,\n          country: { code: 'US' }\n        },\n        emailAddress1: source.email,\n        homePhone: source.phone\n      },\n      producerCodes: [{ id: this.mapProducerCode(source.agentCode) }]\n    };\n  }\n\n  private mapStateCode(sourceState: string): string {\n    \u002F\u002F Map source system state codes to Guidewire codes\n    const stateMap: Record\u003Cstring, string> = {\n      'California': 'CA',\n      'CA': 'CA',\n      \u002F\u002F Add more mappings\n    };\n    return stateMap[sourceState] || sourceState;\n  }\n}\n```\n\n### Step 4: Data Validation\n\n```gosu\n\u002F\u002F Data validation framework\npackage gw.data.validation\n\nuses gw.api.util.Logger\n\nclass DataValidator {\n  private static final var LOG = Logger.forCategory(\"DataValidator\")\n\n  \u002F\u002F Validate account data\n  static function validateAccount(account : Account) : ValidationResult {\n    var result = new ValidationResult()\n\n    \u002F\u002F Required fields\n    if (account.AccountHolderContact == null) {\n      result.addError(\"AccountHolderContact\", \"Account holder contact is required\")\n    }\n\n    if (account.AccountNumber == null || account.AccountNumber.Empty) {\n      result.addError(\"AccountNumber\", \"Account number is required\")\n    }\n\n    \u002F\u002F Contact validation\n    if (account.AccountHolderContact != null) {\n      var contact = account.AccountHolderContact\n      if (contact typeis Person) {\n        if (contact.FirstName == null || contact.FirstName.Empty) {\n          result.addError(\"FirstName\", \"First name is required for person\")\n        }\n        if (contact.LastName == null || contact.LastName.Empty) {\n          result.addError(\"LastName\", \"Last name is required for person\")\n        }\n      } else if (contact typeis Company) {\n        if (contact.Name == null || contact.Name.Empty) {\n          result.addError(\"CompanyName\", \"Company name is required\")\n        }\n      }\n\n      \u002F\u002F Address validation\n      if (contact.PrimaryAddress != null) {\n        validateAddress(contact.PrimaryAddress, result)\n      } else {\n        result.addError(\"PrimaryAddress\", \"Primary address is required\")\n      }\n    }\n\n    return result\n  }\n\n  private static function validateAddress(address : Address, result : ValidationResult) {\n    if (address.AddressLine1 == null || address.AddressLine1.Empty) {\n      result.addError(\"AddressLine1\", \"Address line 1 is required\")\n    }\n    if (address.City == null || address.City.Empty) {\n      result.addError(\"City\", \"City is required\")\n    }\n    if (address.State == null) {\n      result.addError(\"State\", \"State is required\")\n    }\n    if (address.PostalCode == null || address.PostalCode.Empty) {\n      result.addError(\"PostalCode\", \"Postal code is required\")\n    }\n\n    \u002F\u002F Postal code format validation\n    if (address.PostalCode != null && !address.PostalCode.matches(\"^\\\\d{5}(-\\\\d{4})?$\")) {\n      result.addError(\"PostalCode\", \"Invalid postal code format\")\n    }\n  }\n\n  \u002F\u002F Validate policy data\n  static function validatePolicy(policy : Policy) : ValidationResult {\n    var result = new ValidationResult()\n\n    if (policy.Account == null) {\n      result.addError(\"Account\", \"Policy must be associated with an account\")\n    }\n\n    if (policy.EffectiveDate == null) {\n      result.addError(\"EffectiveDate\", \"Effective date is required\")\n    }\n\n    if (policy.ExpirationDate == null) {\n      result.addError(\"ExpirationDate\", \"Expiration date is required\")\n    }\n\n    if (policy.EffectiveDate != null && policy.ExpirationDate != null) {\n      if (policy.ExpirationDate.before(policy.EffectiveDate)) {\n        result.addError(\"ExpirationDate\", \"Expiration date must be after effective date\")\n      }\n    }\n\n    return result\n  }\n}\n\nclass ValidationResult {\n  private var _errors = new ArrayList\u003CValidationError>()\n\n  property get HasErrors() : boolean { return !_errors.Empty }\n  property get Errors() : List\u003CValidationError> { return _errors }\n\n  function addError(field : String, message : String) {\n    _errors.add(new ValidationError(field, message))\n  }\n\n  function throwIfInvalid() {\n    if (HasErrors) {\n      throw new ValidationException(\n        \"Validation failed: \" + _errors.map(\\e -> e.toString()).join(\", \")\n      )\n    }\n  }\n}\n```\n\n### Step 5: Data Export\n\n```gosu\n\u002F\u002F Data export utilities\npackage gw.data.export\n\nuses gw.api.database.Query\nuses java.io.FileWriter\nuses java.io.BufferedWriter\n\nclass DataExporter {\n\n  \u002F\u002F Export to CSV\n  static function exportAccountsToCSV(filename : String, filter : AccountFilter = null) {\n    var writer = new BufferedWriter(new FileWriter(filename))\n\n    try {\n      \u002F\u002F Write header\n      writer.write(\"AccountNumber,FirstName,LastName,Email,Phone,Address,City,State,Zip\\\\n\")\n\n      \u002F\u002F Query accounts\n      var query = Query.make(Account)\n      if (filter != null) {\n        if (filter.Status != null) {\n          query.compare(Account#AccountStatus, Equals, filter.Status)\n        }\n        if (filter.CreatedAfter != null) {\n          query.compare(Account#CreateTime, GreaterThan, filter.CreatedAfter)\n        }\n      }\n\n      \u002F\u002F Stream results to file\n      query.select().each(\\account -> {\n        var contact = account.AccountHolderContact\n        var address = contact.PrimaryAddress\n\n        writer.write(escapeCSV(account.AccountNumber) + \",\")\n        writer.write(escapeCSV(contact typeis Person ? contact.FirstName : \"\") + \",\")\n        writer.write(escapeCSV(contact typeis Person ? contact.LastName : contact.Name) + \",\")\n        writer.write(escapeCSV(contact.EmailAddress1 ?: \"\") + \",\")\n        writer.write(escapeCSV(contact.HomePhone ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.AddressLine1 ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.City ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.State?.Code ?: \"\") + \",\")\n        writer.write(escapeCSV(address?.PostalCode ?: \"\") + \"\\\\n\")\n      })\n    } finally {\n      writer.close()\n    }\n  }\n\n  \u002F\u002F Export to JSON\n  static function exportAccountsToJSON(filename : String, limit : int = 1000) : int {\n    var accounts = Query.make(Account)\n      .compare(Account#AccountStatus, Equals, AccountStatus.TC_ACTIVE)\n      .select()\n      .take(limit)\n      .toList()\n\n    var json = accounts.map(\\a -> accountToMap(a))\n    var jsonString = gw.api.json.JsonObject.toJson(json)\n\n    var writer = new FileWriter(filename)\n    writer.write(jsonString)\n    writer.close()\n\n    return accounts.Count\n  }\n\n  private static function accountToMap(account : Account) : Map\u003CString, Object> {\n    var contact = account.AccountHolderContact\n    return {\n      \"accountNumber\" -> account.AccountNumber,\n      \"status\" -> account.AccountStatus.Code,\n      \"contact\" -> {\n        \"name\" -> contact.DisplayName,\n        \"email\" -> contact.EmailAddress1,\n        \"phone\" -> contact.HomePhone\n      },\n      \"createdDate\" -> account.CreateTime.toString()\n    }\n  }\n\n  private static function escapeCSV(value : String) : String {\n    if (value.contains(\",\") || value.contains(\"\\\"\") || value.contains(\"\\\\n\")) {\n      return \"\\\"\" + value.replace(\"\\\"\", \"\\\"\\\"\") + \"\\\"\"\n    }\n    return value\n  }\n}\n```\n\n### Step 6: Data Governance\n\n```typescript\n\u002F\u002F Data governance and PII handling\ninterface PIIField {\n  fieldPath: string;\n  classification: 'PII' | 'PHI' | 'PCI' | 'SENSITIVE';\n  maskingRule: 'full' | 'partial' | 'hash';\n}\n\nconst piiFields: PIIField[] = [\n  { fieldPath: 'contact.ssn', classification: 'PII', maskingRule: 'partial' },\n  { fieldPath: 'contact.dateOfBirth', classification: 'PII', maskingRule: 'partial' },\n  { fieldPath: 'contact.emailAddress1', classification: 'PII', maskingRule: 'partial' },\n  { fieldPath: 'bankAccount.accountNumber', classification: 'PCI', maskingRule: 'partial' },\n  { fieldPath: 'bankAccount.routingNumber', classification: 'PCI', maskingRule: 'full' }\n];\n\nclass PIIMasker {\n  mask(data: any, fields: PIIField[]): any {\n    const masked = JSON.parse(JSON.stringify(data));\n\n    fields.forEach(field => {\n      const value = this.getNestedValue(masked, field.fieldPath);\n      if (value) {\n        const maskedValue = this.applyMaskingRule(value, field.maskingRule);\n        this.setNestedValue(masked, field.fieldPath, maskedValue);\n      }\n    });\n\n    return masked;\n  }\n\n  private applyMaskingRule(value: string, rule: string): string {\n    switch (rule) {\n      case 'full':\n        return '*'.repeat(value.length);\n      case 'partial':\n        if (value.length \u003C= 4) return '****';\n        return '*'.repeat(value.length - 4) + value.slice(-4);\n      case 'hash':\n        return this.hashValue(value);\n      default:\n        return '****';\n    }\n  }\n\n  private hashValue(value: string): string {\n    \u002F\u002F SHA-256 hash for one-way masking\n    const crypto = require('crypto');\n    return crypto.createHash('sha256').update(value).digest('hex').substring(0, 16);\n  }\n}\n\n\u002F\u002F Data retention policy\nclass DataRetentionPolicy {\n  private retentionRules: Map\u003Cstring, number> = new Map([\n    ['claim', 7 * 365],        \u002F\u002F 7 years\n    ['policy', 10 * 365],      \u002F\u002F 10 years\n    ['auditLog', 90],          \u002F\u002F 90 days\n    ['sessionData', 1],        \u002F\u002F 1 day\n  ]);\n\n  async enforceRetention(): Promise\u003CRetentionResult> {\n    const result: RetentionResult = { deleted: {}, errors: [] };\n\n    for (const [entityType, retentionDays] of this.retentionRules) {\n      try {\n        const cutoffDate = new Date();\n        cutoffDate.setDate(cutoffDate.getDate() - retentionDays);\n\n        const deletedCount = await this.deleteOldRecords(entityType, cutoffDate);\n        result.deleted[entityType] = deletedCount;\n      } catch (error) {\n        result.errors.push({ entityType, error: error.message });\n      }\n    }\n\n    return result;\n  }\n}\n```\n\n## Data Handling Best Practices\n\n| Practice | Description |\n|----------|-------------|\n| Use Bundles | Always use Transaction.runWithNewBundle for writes |\n| Batch Processing | Process large datasets in batches of 100-500 |\n| Lazy Loading | Use select() and iterate, don't toList() large results |\n| Index Awareness | Query on indexed fields for performance |\n| Validation First | Validate data before database operations |\n| Audit Trail | Log all data modifications |\n| PII Protection | Mask or encrypt sensitive data |\n\n## Output\n\n- Entity CRUD operations\n- Batch processing framework\n- Data migration utilities\n- Validation framework\n- Export capabilities\n- Data governance tools\n\n## Resources\n\n- [Guidewire Data Model](https:\u002F\u002Fdocs.guidewire.com\u002Fcloud\u002F)\n- [Query API Documentation](https:\u002F\u002Fdocs.guidewire.com\u002Fcloud\u002Fpc\u002F202503\u002Fapiref\u002F)\n\n## Next Steps\n\nFor role-based access control, see `guidewire-enterprise-rbac`.\n",{"name":5,"author":51,"license":15,"version":52,"description":53,"allowed-tools":54},"Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Data handling best practices for Guidewire InsuranceSuite including entity management,\ndata migration, batch operations, and data governance.\nTrigger with phrases like \"guidewire data\", \"entity management\",\n\"data migration\", \"batch processing\", \"data governance guidewire\".\n","Read, Write, Edit, Bash(curl:*), Bash(gradle:*), Grep","main","90b50ed0778745a80fd5bfb13b1843d45b9e82f7","tests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fguidewire-pack\u002Fskills\u002Fguidewire-data-handling",234,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust","https:\u002F\u002Fgithub.com\u002FDicklesworthstone","https:\u002F\u002Fwww.jeffreyemanuel.com\u002F","Building in NY","2026-11-13 11:50:27",{"source":8,"skillName":5,"commands":65,"prompt":75,"zipAvailable":76},[66,69,72],{"tool":67,"command":68},"npx","npx skills add Dicklesworthstone\u002Fpi_agent_rust --skill guidewire-data-handling",{"tool":70,"command":71},"bunx","bunx skills add Dicklesworthstone\u002Fpi_agent_rust --skill guidewire-data-handling",{"tool":73,"command":74},"pnpm","pnpm dlx skills add Dicklesworthstone\u002Fpi_agent_rust --skill guidewire-data-handling","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fguidewire-pack\u002Fskills\u002Fguidewire-data-handling\u002FSKILL.md",true,[],[79,119,143,169,196,217],{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":83,"licenseFileUrl":84,"sourceHtmlUrl":85,"categories":86,"keywords":92,"publishedAt":46},"01M6FEGPPK0ZTKCCM0R3E3KS33","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FSKILL.md",[87],{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,27,117,118],"web artifacts builder","web","artifacts","artifacts builder","builder","frontend","css","components","react","component","routing","state management","state","shadcn","file","ui","Tools","Tailwind","use","html","single-file","simple","ui components","multi","jsx","suite",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":83,"licenseFileUrl":123,"sourceHtmlUrl":124,"categories":125,"keywords":130,"publishedAt":46},"01M6FEE9VTK1W4GY21AKREJXRG","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FSKILL.md",[126,129],{"categoryNo":127,"categoryName":128,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":88,"categoryName":89,"taxonomyType":90,"taxonomyTypeDesc":91},[131,132,133,98,134,135,94,136,137,138,108,139,140,141,142,29,27],"testing","webapp testing","webapp","screenshots","playwright","debugging","toolkit","browser","logs","applications","behavior","local",{"skillNo":144,"name":145,"nameZh":6,"description":146,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":147,"sourceHtmlUrl":148,"categories":149,"keywords":150,"publishedAt":46},"01M6FE20F7ZTQBFWKF4MPJ2SBX","neuropixels-analysis","Neuropixels neural recording analysis. Load SpikeGLX\u002FOpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen\u002FIBL curation, AI-assisted visual analysis, for Neuropixels 1.0\u002F2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FSKILL.md",[],[151,152,153,154,25,155,156,157,111,158,159,160,161,162,163,164,29,165,166,167,27,168],"analysis","neuropixels analysis","spike","metrics","open","quality","user","recordings","Neural","motion","visual","quality metrics","unit","sorting","curation","recording","load","when",{"skillNo":170,"name":171,"nameZh":6,"description":172,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":83,"licenseFileUrl":173,"sourceHtmlUrl":174,"categories":175,"keywords":179,"publishedAt":46},"01M6FDP0PDK6F1GGX395C7625Z","skill-creator","Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FSKILL.md",[176],{"categoryNo":177,"categoryName":178,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000023","Skill与智能体开发",[180,181,182,183,184,185,186,187,188,189,190,191,192,193,29,27,194,168,195],"skill creator","creator","workflows","create","update","capabilities","knowledge","tool","integrations","specialized","guide","new skill","existing skill","users","Existing","new",{"skillNo":197,"name":198,"nameZh":6,"description":199,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":200,"sourceHtmlUrl":201,"categories":202,"keywords":204,"publishedAt":46},"01M6FDM8Y7VK7NYG9SM7QPA3PW","notebooklm","Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FSKILL.md",[203],{"categoryNo":177,"categoryName":178,"taxonomyType":90,"taxonomyTypeDesc":91},[198,205,206,207,208,209,210,138,211,26,111,212,213,214,215,27,216],"google","browser automation","gemini","automation","auth","code","library","only","document","query","Citation","source",{"skillNo":218,"name":219,"nameZh":6,"description":220,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":83,"licenseFileUrl":221,"sourceHtmlUrl":222,"categories":223,"keywords":230,"publishedAt":46},"01M6FDHPXE10J3XGTCH5HBHS1M","slack-gif-creator","Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.\"","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FSKILL.md",[224,227],{"categoryNo":225,"categoryName":226,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":228,"categoryName":229,"taxonomyType":90,"taxonomyTypeDesc":91},"01JBSEEDCAT000000000000016","图像与多媒体",[231,232,233,181,234,109,235,236,186,111,237,193,238,239,240,27,241,168],"slack gif creator","slack","gif","validation","constraints","Animation","me","make","Utilities","concepts","request"]