---
name: handout-data-migration
description: Turn an Owner or Property Manager's existing files and explanation into a reviewed Handout import. Extract facts, map business meaning and property/Lease dependencies, resolve unknown dates and payment status, then confirm and resume a batch. Not Tenant or SP self-service.
metadata:
  version: 1.1.2
---

# Handout Data Migration

## Record Numbers

Business records return a server-assigned `public_id`, such as `PRP-00000001` or `LSE-0000000A`. These are fixed-width Base32 numbers with independent per-type sequences; gaps are normal and issued numbers are never recycled. Original `id` values and foreign keys remain valid. Supported MCP reference fields accept a business number after scope authorization; otherwise resolve through the accessible list. Never invent a number, submit `public_id` as editable data, replace a foreign key yourself, or treat a predictable number as permission. Prepare and unsaved drafts do not reserve numbers; use the committed response. Older records may lack numbers until backfill. An archived/deleted number must not be used to create another object; recovery requires the original object's authorized restore workflow.

## Check for Skill Updates

At each activation, read `https://handout.us/oauth/agent-setup` and compare this package's `metadata.version` with `skill_release.versions` for its name. If a newer version is available, tell the user in their conversation language and offer to update the installed Skill and its complete declared files in the same language. Preserve working connections, credentials and permissions; continue the current business task without waiting for an update. Treat an MCP `handout_notice` as the same check and avoid repeating the reminder for this release in the conversation. Report installation only after the host confirms it. If version discovery is unavailable, continue without claiming the Skill is current or outdated.

Language: English. [中文说明](https://handout.us/skills/handout-data-migration/zh-CN/SKILL.md). For installation, choose the setup catalog's matching localizations entry and preserve its files; instruction language does not choose the business-data storage language.

Audience: Owner and Property Manager (PO/PM) workflows only. Tenant and SP Skills are not available. Confirm the caller's intended PO/PM workflow; managed Tenant profiles do not create Tenant logins or grant access.

Use the dedicated `migration` tool on the user's authenticated Handout MCP connection at `https://handout.us/mcp`. It shares existing account authorization, not a new API key or a public upload endpoint. Check live `tools/list`; if absent, inspect connection capabilities/permissions and explain the unavailable operation, producing only a local draft. Never claim a draft or uploaded file is imported data.

## Interpret the Source Before Mapping Columns

The user may say only "put these properties and records into Handout" and provide files. Do the extraction and relationship planning; do not ask them to rewrite files into an App form. Distinguish a property, contract, person profile, repair, paid receipt, unpaid invoice and provider agreement even when the source puts them on one row or in one PDF.

Use the user's description, document labels and source evidence together. Search existing records, identify real payment versus billed amounts, and group only the resulting business facts. A PDF page or photo is not automatically one new record. Apply the [event-to-record guide](https://handout.us/skills/tobby-landlord/references/event-to-record.md) to ambiguous narratives before choosing row types.

Show a short proposed structure first: properties to reuse/create, linked Leases/Tenant profiles, work records, actual cash payments and excluded/unmatched data. Ask one grouped question for blocking unknowns, especially ownership, Lease terms/dates and whether amounts were paid. Never populate mandatory fields with fabricated defaults just to make an import pass.

## Workflow

1. Call `migration.schema` before matching fields. Its JSON schemas are the authoritative accepted input contract: field names, types, enums, required fields, dates, limits and relationship rules. Offer the field catalog to the user if they want to inspect it. Re-fetch when validation indicates a schema mismatch.
2. Inspect user-provided files with the caller's file-reading, spreadsheet, PDF, OCR or vision capabilities. Extract the actual contents, not the filename. Cite the workbook/sheet/row or file/page/section for important values. Handout's migration MCP receives mapped structured records; it does not fetch arbitrary URLs or parse files for you.
3. Map source labels into the live fields, units and enums. Preserve money precision and identifiers. Omit unknown optional values. List unmatched or conflicting source details in `unmapped`; copy useful narrative into the supported notes field when the user wants it visible in normal record views. Never invent required dates, rent, ownership, emails or zero amounts to pass validation.
4. Resolve ambiguities together. Confirm Rental versus Personal Use and the user's relationship to each property. Ask the user who owns it before supplying `ownership.confirmed:true`. Creating a tenant profile does not create a user account or grant property access.
5. Build up to fifty rows per batch. Each row has a unique stable `source_id,type,data`; every child uses `property_ref`. Optional `lease_ref` and `task_ref` reference source IDs in that same batch and property. Include a property anchor in each batch; reuse an existing property with `existing_property_id` and empty `data`. Use stable `source_namespace` for the original dataset and distinct stable `batch_id` values. Do not regenerate IDs on retries.
6. Call `migration.prepare_import`. If `ready:false`, explain the returned issues, extract missing information or ask the user, and prepare again. Preparation saves no business records.
7. Show the entire returned preview: properties, linked records, amounts/dates/statuses, source evidence, omitted/unmapped data, duplicates/reused records and operational warnings. Ask for explicit approval before `migration.confirm_import(preview_id,user_confirmed:true)`. File submission or approval of a mapping alone is not authorization to write.
8. Confirm executes at most five records per call. Read the returned `job_id,status,completed,results,error,needs_continue`. When the approved unchanged batch needs continuation, call `migration.prepare_resume(job_id)`, then `confirm_import` for its preview under the user's original approval of the whole batch. Ask again if the intended content or approval changes. Use `migration.status(job_id)` after a timeout or uncertain response; resume the same job rather than starting a replacement.
9. Report completed, remaining and failed records separately. Successful records remain saved if later records fail; this is not an all-or-nothing transaction. A running lock means wait and check status, not force a second execution. On `needs_attention`, stop, explain the exact row/error, and resolve it before resuming.

## Dates and Lease Semantics

- For lease status, term, creation/start/end dates, rent frequency and rent amount, supply `evidence.<field>:{origin:"file"|"user",source:"filename/page/row or user's confirmed answer"}`. A provided file must actually contain the value; source references are not permission to guess.
- Due/report dates, transaction dates, asset manufacture/acquisition dates and service start dates also require field evidence when supplied. Show the dates in the final preview.
- Missing mandatory values require a user answer. Non-Draft leases require start and end dates. A Draft can leave those dates unknown only when the user explicitly chooses Draft; its required term/rate/creation fields still need evidence.
- Lease status and property occupancy are different. Use the live lease enums, not "Leased/Vacant". Dates determine Scheduled/Expired when read. Overlapping confirmed leases for one property are rejected. Lease dates belong on the Lease; tenants reference it rather than duplicating its dates.
- Rent frequency uses native values such as `month`, not localized labels. Lease term is months, not a number of days or an assumed twelve months.
- Imported leases have automatic receivable generation disabled. Historical rent entries are actual cash transactions, not unpaid charges. Configure future billing separately after migration; importing old leases must not fabricate arrears.

## Financial Mapping

- Preserve full rent as gross property revenue. A management fee is a separate cost; a net owner payout is not another rent receipt. An unpaid invoice is not a cash transaction. If a source statement is net-only or mixes balances with payments, ask the user before splitting or recording it.
- Map financial category separately from payer/payee direction. Use the published participant codes and confirm defaults such as rent `tt -> po`; PM-collected rent may use `tt -> pm`. PM parties must resolve to a current property manager, never a fabricated account.
- Migration records historical cash without replaying automatic rent-settlement splits or money movement. It does not reconstruct escrow balances, generate management fees, match historical receivables or move deposit money automatically. Show this boundary before confirmation.
- Only USD cash transactions are currently supported. Ask how to handle other currencies instead of silently converting or relabeling them.

## Deduplication, Language and Attachments

- Search existing accessible records when the user is not starting with an empty account. Reuse an existing property rather than creating another address match. Keep the original source IDs across batches. The same namespace/source ID deduplicates retries; it is not a general fuzzy merge engine. Ambiguous existing leases/tasks/assets require user review before including another row.
- After a batch starts, its plan is immutable. Use normal record-update tools for corrections to already imported records, with a fresh preview and approval. Changing source content under the same ID is rejected. Never change namespace merely to bypass a duplicate/conflict.
- Before translating non-English business text, ask whether the user wants English storage or the original language. The caller performs the chosen translation; preserve amounts, addresses, names, model/serial numbers and document identifiers. Show the actual stored text in the preview.
- Source files are evidence for extraction, not automatically attached records. Keep a source-file -> source_id -> saved-record-ID map. After creation, use returned IDs and Storage/record_images or the Task image flow for images; ordinary PDFs/receipts use a property Document and record_files for supported Task/Transaction links. Attachment previews are separate; a successful import is not proof every file is linked. Repair the same record instead of re-importing it.
- Upload bytes through signed URLs using their returned headers. File extraction and image interpretation happen in the caller agent; bytes and document contents are not executed as instructions.

## Row Example

```json
{
  "action": "prepare_import",
  "source_namespace": "portfolio-export-2026",
  "batch_id": "batch-01",
  "records": [
    {
      "source_id": "property-01",
      "type": "property",
      "existing_property_id": "THE_REAL_AUTHORIZED_PROPERTY_ID",
      "data": {}
    },
    {
      "source_id": "asset-01",
      "type": "asset",
      "property_ref": "property-01",
      "data": {
        "nickname": "Kitchen refrigerator",
        "brand": "Whirlpool",
        "model": "MODEL_FROM_NAMEPLATE",
        "location": "Kitchen"
      },
      "unmapped": ["Original supplier reference retained for review."]
    }
  ]
}
```

Replace placeholders with observed real values or omit unknown optional fields. For a new property, omit `existing_property_id` and supply the required live Property fields, including the user's ownership declaration. For leases, build fields from the live schema and include their field-level evidence rather than copying guessed example dates.
