Back to index

Migration Reconciliation and Cutover Gate Register

Table
Generates documents
Migration Reconciliation and Cutover Gate Register

Seen as a design

Take it with you

Copy and paste, or save a file and hand it over. For a machine with no network, one “Everything” file is enough.

Everything

migration-reconciliation-register-everything.md

Everything in this panel, including the contents behind each URL, in one Markdown file

Prompt

1.6 KB
Build an HTML document with "Migration Reconciliation and Cutover Gate Register". This layout runs, so there is no composition to rebuild — what it needs is content.

## What this layout does
Tie measured source-to-target values, tolerances, outcomes, remediation, and approved exceptions to a migration cutover decision.

## What you have to supply
- Record source and target, comparable values and units, tolerance, owner, action, and any exception approval.

## The shape of the input
- `title` — string — 移行照合の結論
- `subtitle` — string? — 対象・移行窓・照合時刻
- `sourceSystem` — string — 移行元システム
- `targetSystem` — string — 移行先システム
- `checks` — array (3–12) — 指標、元先の値、算出差分、許容差、責任者、処置、合否、例外承認根拠

schema.ts holds the machine-checkable validation. Ask rather than guess when an input is missing.

## When not to use it
- Use data lineage to explain integration paths and transformation order.

## How to build it
Put the input in a JSON file and run:

```bash
node scripts/render.mjs migration-reconciliation-register <input.json> --theme ink -o out.html
```

The output is one self-contained file and fetches nothing at render time.

## The source
- https://design-vault.pages.dev/designs/migration-reconciliation-register/source/template.tsx
- https://design-vault.pages.dev/designs/migration-reconciliation-register/source/schema.ts
- https://design-vault.pages.dev/designs/migration-reconciliation-register/source/sample-data.json
- https://design-vault.pages.dev/designs/migration-reconciliation-register/sample.html

Files

Template, input schema, sample data and metadata. This document was generated from those four. The generated document

template.tsx

7.2 KB

/designs/migration-reconciliation-register/source/template.tsx

Show contents
import type { Props } from "./schema";

const CSS = String.raw`
.asset-migration-reconciliation-register{font-family:var(--font-sans);color:var(--color-primary);background:var(--color-surface);max-width:var(--container-slide);margin:auto;padding:3rem 2.5rem;line-height:1.55;font-size:var(--text-body-sm)}
.asset-migration-reconciliation-register *{box-sizing:border-box}.asset-migration-reconciliation-register header{margin-bottom:1.2rem}.asset-migration-reconciliation-register .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);margin:0 0 .6rem}.asset-migration-reconciliation-register h1{font-size:var(--text-h1);line-height:1.25;max-width:28em;margin:0}.asset-migration-reconciliation-register .subtitle{color:var(--color-secondary);margin:.65rem 0 0}.asset-migration-reconciliation-register .systems{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:1rem;padding:.8rem 0;border-block:1px solid var(--color-outline);margin:1rem 0}.asset-migration-reconciliation-register .systems span{font-size:var(--text-label);color:var(--color-secondary)}.asset-migration-reconciliation-register .systems strong{display:block;color:var(--color-primary);overflow-wrap:anywhere}.asset-migration-reconciliation-register .checks{border-top:2px solid var(--color-primary)}.asset-migration-reconciliation-register .check{display:grid;grid-template-columns:minmax(7rem,.8fr) minmax(7rem,1fr) minmax(11rem,1.3fr) minmax(7rem,.8fr) minmax(10rem,1.2fr);gap:.65rem;align-items:start;padding:.75rem .4rem;border-bottom:1px solid var(--color-outline);min-width:0}.asset-migration-reconciliation-register .check>*{min-width:0;overflow-wrap:anywhere}.asset-migration-reconciliation-register code{font-family:var(--font-mono);font-size:var(--text-label)}.asset-migration-reconciliation-register .measure{font-weight:600}.asset-migration-reconciliation-register .values{font-family:var(--font-mono);font-size:var(--text-label);font-variant-numeric:tabular-nums}.asset-migration-reconciliation-register .values span{display:block;color:var(--color-secondary)}.asset-migration-reconciliation-register .delta{font-family:var(--font-mono);font-size:var(--text-label)}.asset-migration-reconciliation-register .delta strong{display:block;color:var(--color-primary)}.asset-migration-reconciliation-register .status{display:inline-block;border:1px solid var(--color-outline);padding:.05rem .4rem;font-size:var(--text-label);color:var(--color-secondary)}.asset-migration-reconciliation-register .owner{font-size:var(--text-label);color:var(--color-secondary);margin-top:.3rem}.asset-migration-reconciliation-register .action{font-size:var(--text-label)}.asset-migration-reconciliation-register .waiver{margin-top:.3rem;padding-left:.5rem;border-left:2px solid var(--color-tertiary);color:var(--color-secondary)}.asset-migration-reconciliation-register .still-svg{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media(max-width:760px){.asset-migration-reconciliation-register{padding:1.5rem 1rem}.asset-migration-reconciliation-register h1{font-size:var(--text-h2)}.asset-migration-reconciliation-register .systems{grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:.5rem}.asset-migration-reconciliation-register .check{grid-template-columns:minmax(0,1fr) auto;gap:.35rem .7rem;padding:.8rem .2rem}.asset-migration-reconciliation-register .measure{grid-column:1}.asset-migration-reconciliation-register .status{grid-column:2;grid-row:1}.asset-migration-reconciliation-register .values{grid-column:1}.asset-migration-reconciliation-register .delta{grid-column:2;grid-row:2;text-align:right}.asset-migration-reconciliation-register .owner{grid-column:1/3}.asset-migration-reconciliation-register .action{grid-column:1/3}}
@media print{.asset-migration-reconciliation-register{max-width:none;padding:1rem}.asset-migration-reconciliation-register .check{break-inside:avoid}}
`;

const statusLabel = { pass: "合格", review: "要確認", waived: "例外承認" };
const compact = (value: string, max: number) => [...value].length > max ? `${[...value].slice(0, max - 1).join("")}…` : value;

function Still({ data }: { data: Props }) {
  return <svg className="still-svg" data-layout-still="true" width="1280" height="720" viewBox="0 0 1280 720" aria-hidden="true">
    <rect width="1280" height="720" fill="var(--color-surface)" />
    <text x="48" y="43" fontSize="13" fontFamily="sans-serif" fill="var(--color-secondary)">{data.sourceSystem} → {data.targetSystem}</text>
    <text x="48" y="83" fontSize="26" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(data.title, 66)}</text>
    <text x="48" y="118" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">照合値 · 差分 · 許容差 · 判定 · 責任者</text>
    {data.checks.slice(0, 8).map((check, index) => {
      const y = 164 + index * 62;
      return <g key={check.id}>
        <line x1="48" x2="1232" y1={y - 19} y2={y - 19} stroke="var(--color-outline)" />
        <text x="52" y={y + 3} fontSize="11" fontFamily="monospace" fill="var(--color-tertiary)">{check.id}</text>
        <text x="145" y={y + 3} fontSize="12" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(check.measure, 31)}</text>
        <text x="430" y={y + 3} fontSize="11" fontFamily="monospace" fill="var(--color-secondary)">{check.sourceValue} → {check.targetValue}</text>
        <text x="670" y={y + 3} fontSize="12" fontFamily="monospace" fill="var(--color-on-surface)">Δ {check.delta} / ±{check.tolerance}</text>
        <text x="930" y={y + 3} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{statusLabel[check.status]} · {compact(check.owner, 27)}</text>
      </g>;
    })}
    <text x="48" y="704" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">{data.checks.length}照合項目 · 差分と例外承認を切替ゲートに記録</text>
  </svg>;
}

export default function Template(data: Props) {
  return <section className="asset asset-migration-reconciliation-register">
    <style>{CSS}</style>
    <header>
      <p className="eyebrow">移行データ照合 · {data.checks.length}項目</p>
      <h1>{data.title}</h1>
      {data.subtitle && <p className="subtitle">{data.subtitle}</p>}
    </header>
    <div className="systems"><div><span>移行元</span><strong>{data.sourceSystem}</strong></div><span aria-hidden="true">→</span><div><span>移行先</span><strong>{data.targetSystem}</strong></div></div>
    <div className="checks">
      {data.checks.map((check) => <article className="check" key={check.id}>
        <div><code>{check.id}</code><div className="measure">{check.measure}</div><div className="owner">判断責任: {check.owner}</div></div>
        <div className="values"><span>元: {check.sourceValue} {check.unit}</span><span>先: {check.targetValue} {check.unit}</span></div>
        <div className="delta"><strong>差分 {check.delta} {check.unit}</strong><span>許容 ±{check.tolerance} {check.unit}</span></div>
        <span className="status" data-status={check.status}>{statusLabel[check.status]}</span>
        <div className="action">{check.action}{check.waiver && <div className="waiver">承認根拠: {check.waiver}</div>}</div>
      </article>)}
    </div>
    <Still data={data} />
  </section>;
}

schema.ts

2.6 KB

/designs/migration-reconciliation-register/source/schema.ts

Show contents
import { z } from "zod";

const text = z.string().trim().min(1).max(160);
const short = z.string().trim().min(1).max(48);
const checkSchema = z.object({
  id: short.describe("照合項目の一意な識別子"),
  measure: short.describe("件数・金額・ハッシュなど比較する値"),
  unit: short.describe("値の単位または比較方式"),
  sourceValue: z.number().describe("移行元で観測した値"),
  targetValue: z.number().describe("移行先で観測した値"),
  delta: z.number().describe("移行先値から移行元値を引いた差"),
  tolerance: z.number().nonnegative().describe("受け入れ可能な絶対差"),
  owner: short.describe("照合の判断責任者"),
  action: text.describe("不一致時または完了時の次の行動"),
  status: z.enum(["pass", "review", "waived"]).describe("許容差と例外承認に対する判定"),
  waiver: text.optional().describe("差分を受け入れる承認根拠"),
}).strict();

export const schema = z.object({
  title: text.describe("移行照合の結論"),
  subtitle: text.optional().describe("切替対象・移行窓・照合時刻"),
  sourceSystem: short.describe("移行元システム"),
  targetSystem: short.describe("移行先システム"),
  checks: z.array(checkSchema).min(3).max(12).describe("元先で照合する移行データと受け入れ判定"),
}).strict().superRefine((data, ctx) => {
  const ids = data.checks.map(({ id }) => id);
  if (new Set(ids).size !== ids.length) {
    ctx.addIssue({ code: "custom", path: ["checks"], message: "照合IDは一意にすること" });
  }
  data.checks.forEach((check, index) => {
    const difference = check.targetValue - check.sourceValue;
    if (Math.abs(check.delta - difference) > 1e-9) {
      ctx.addIssue({ code: "custom", path: ["checks", index, "delta"], message: "差分は移行先値−移行元値に一致させること" });
    }
    const withinTolerance = Math.abs(difference) <= check.tolerance;
    if (check.status === "pass" && !withinTolerance) {
      ctx.addIssue({ code: "custom", path: ["checks", index, "status"], message: "許容差を超えた項目を合格にできない" });
    }
    if (check.status === "review" && withinTolerance) {
      ctx.addIssue({ code: "custom", path: ["checks", index, "status"], message: "許容差内の項目は合格または例外承認にすること" });
    }
    if (check.status === "waived" && (!check.waiver || withinTolerance)) {
      ctx.addIssue({ code: "custom", path: ["checks", index, "waiver"], message: "例外承認には許容差を超えた差分の承認根拠が必要" });
    }
  });
});

export type Props = z.infer<typeof schema>;

meta.yaml

1.8 KB

/designs/migration-reconciliation-register/source/meta.yaml

Show contents
id: migration-reconciliation-register
name:
  ja: 移行データ照合・切替ゲート台帳
  en: Migration Reconciliation and Cutover Gate Register
purpose:
  ja: 移行元と移行先の実測値、許容差、合否、差分の処置と例外承認を切替判断に結びつける。
  en: Tie measured source-to-target values, tolerances, outcomes, remediation, and approved exceptions to a migration cutover decision.
status: published
still: ./still.png
category: governance
catalogOrder: 845
jobs:
  - prioritize-diagnose
  - assign-governance
useCases:
  - migration
  - requirements
layoutKinds:
  - table
  - stack
tags:
  - data-migration
  - reconciliation
  - cutover
  - data-parity
  - responsive
whenToUse:
  - ja: 移行リハーサルや本番切替で、元先の値の差分を許容差と承認記録つきで判定するとき。
    en: Use during migration rehearsals or cutovers to judge source-to-target differences against tolerances and approvals.
avoidWhen:
  - ja: システム間のデータ連携経路と変換順序を説明する場合は、データリネージを使う。
    en: Use data lineage to explain integration paths and transformation order.
requiredInputs:
  - ja: 移行元・移行先、同じ単位で比較した値、許容差、責任者、処置、例外承認根拠を記録する。
    en: Record source and target, comparable values and units, tolerance, owner, action, and any exception approval.
relatedLayouts:
  - data-lineage-stages
  - requirement-traceability-matrix
dataSchema:
  title: string — 移行照合の結論
  subtitle: string? — 対象・移行窓・照合時刻
  sourceSystem: string — 移行元システム
  targetSystem: string — 移行先システム
  checks: array (3–12) — 指標、元先の値、算出差分、許容差、責任者、処置、合否、例外承認根拠
sources: []

sample-data.json

1.1 KB

/designs/migration-reconciliation-register/source/sample-data.json

Show contents
{
  "title": "請求明細の件数と残高は許容差内、通貨コード異常2件は切替前に修正する",
  "subtitle": "Billing v1 → Billing Cloud · rehearsal 2026-09-21 02:00 UTC",
  "sourceSystem": "Billing v1",
  "targetSystem": "Billing Cloud",
  "checks": [
    { "id": "REC-001", "measure": "Open invoice rows", "unit": "rows", "sourceValue": 18240, "targetValue": 18240, "delta": 0, "tolerance": 0, "owner": "Data Migration", "action": "合格。切替チェックシートへ照合ログを添付する。", "status": "pass" },
    { "id": "REC-002", "measure": "Open balance", "unit": "USD", "sourceValue": 748250.5, "targetValue": 748250.49, "delta": -0.01, "tolerance": 0.05, "owner": "Finance Ops", "action": "許容差内。通貨丸めの説明を監査記録へ残す。", "status": "pass" },
    { "id": "REC-003", "measure": "Invalid currency codes", "unit": "rows", "sourceValue": 0, "targetValue": 2, "delta": 2, "tolerance": 0, "owner": "Billing Platform", "action": "対象請求を修正して照合を再実行する。", "status": "review" }
  ]
}