Back to index

Data Retention and Disposal Register

Table
Generates documents
Data Retention and Disposal 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

data-retention-register-everything.md

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

Prompt

1.5 KB
Build an HTML document with "Data Retention and Disposal Register". This layout runs, so there is no composition to rebuild — what it needs is content.

## What this layout does
Record the retention trigger, duration, disposition, approved basis, and exceptions for each data class.

## What you have to supply
- Provide the data class, repository, owner, start event, approved duration, disposition, and policy basis.

## The shape of the input
- `title` — string — 台帳の目的
- `subtitle` — string? — 対象範囲・基準日・前提
- `scope` — string — 対象システム・組織
- `records` — array (3–10) — 保存対象、起算、期間、処理、根拠、例外

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

## When not to use it
- Do not use this register to determine legal obligations or prescribe retention periods.

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

```bash
node scripts/render.mjs data-retention-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/data-retention-register/source/template.tsx
- https://design-vault.pages.dev/designs/data-retention-register/source/schema.ts
- https://design-vault.pages.dev/designs/data-retention-register/source/sample-data.json
- https://design-vault.pages.dev/designs/data-retention-register/sample.html

Files

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

template.tsx

8.9 KB

/designs/data-retention-register/source/template.tsx

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

const CSS = String.raw`
.asset-data-retention-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.6;font-size:var(--text-body-sm)}
.asset-data-retention-register *{box-sizing:border-box}.asset-data-retention-register header{margin-bottom:1.5rem}.asset-data-retention-register .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);margin-bottom:.75rem}.asset-data-retention-register h1{font-size:var(--text-h1);line-height:1.3;max-width:28em}.asset-data-retention-register .subtitle{margin-top:.75rem;color:var(--color-secondary)}
.asset-data-retention-register .summary{display:flex;gap:1.5rem;flex-wrap:wrap;padding:.75rem 0;border-top:2px solid var(--color-primary);border-bottom:1px solid var(--color-outline);margin-bottom:1rem}.asset-data-retention-register .summary p{margin:0;color:var(--color-secondary);font-size:var(--text-label)}.asset-data-retention-register .summary strong{font-family:var(--font-mono);font-size:var(--text-h3);color:var(--color-primary);margin-right:.35rem}
.asset-data-retention-register .record{display:grid;grid-template-columns:minmax(9rem,1fr) minmax(0,1.25fr) minmax(7rem,.7fr) minmax(8rem,.8fr) minmax(11rem,1.1fr);gap:.9rem;padding:1rem 0;border-bottom:1px solid var(--color-outline);align-items:start;min-width:0}.asset-data-retention-register .record>*{min-width:0}.asset-data-retention-register .record-head{font-weight:650;overflow-wrap:anywhere}.asset-data-retention-register .id{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary)}.asset-data-retention-register .system,.asset-data-retention-register .owner,.asset-data-retention-register .basis{color:var(--color-secondary);font-size:var(--text-label);overflow-wrap:anywhere}.asset-data-retention-register .field-label{display:block;color:var(--color-secondary);font-size:var(--text-label);margin-bottom:.2rem}.asset-data-retention-register .event{overflow-wrap:anywhere}.asset-data-retention-register .term{font-family:var(--font-mono);font-size:var(--text-h3);font-variant-numeric:tabular-nums;white-space:nowrap}.asset-data-retention-register .disposal{font-weight:650}.asset-data-retention-register .exception{font-size:var(--text-label);border-left:2px solid var(--color-outline);padding-left:.6rem;overflow-wrap:anywhere}.asset-data-retention-register .exception-active{border-color:var(--color-tertiary)}.asset-data-retention-register .basis{margin-top:.45rem}.asset-data-retention-register footer{margin-top:1.1rem;padding-top:.75rem;border-top:1px solid var(--color-outline);color:var(--color-secondary);font-size:var(--text-label)}.asset-data-retention-register .still-svg{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media(max-width:760px){.asset-data-retention-register{padding:1.5rem 1rem}.asset-data-retention-register h1{font-size:var(--text-h2)}.asset-data-retention-register .record{grid-template-columns:minmax(0,1fr) auto;gap:.6rem 1rem;padding:.9rem 0}.asset-data-retention-register .record-head{grid-column:1}.asset-data-retention-register .term{grid-column:2;grid-row:1/3;text-align:right}.asset-data-retention-register .event{grid-column:1/3}.asset-data-retention-register .disposal{grid-column:1}.asset-data-retention-register .exception{grid-column:2;grid-row:4/6}.asset-data-retention-register .basis{grid-column:1/3;margin-top:0}.asset-data-retention-register .system,.asset-data-retention-register .owner{display:inline}.asset-data-retention-register .summary{gap:1rem}}
@media(print){.asset-data-retention-register{max-width:none;padding:1rem}.asset-data-retention-register .record{break-inside:avoid}}
`;

const units = { days: "日", months: "か月", years: "年" };
const disposalNames = { delete: "削除", anonymize: "匿名化", archive: "アーカイブ" };
const compact = (value: string, max: number) => [...value].length > max ? `${[...value].slice(0, max - 1).join("")}…` : value;

function Still({ data }: { data: Props }) {
  const records = data.records.slice(0, 10);
  const rowHeight = 52;
  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="48" fontSize="14" fontFamily="sans-serif" fill="var(--color-secondary)">{data.scope}</text>
    <text x="48" y="87" fontSize="28" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(data.title, 54)}</text>
    <text x="48" y="116" fontSize="13" fontFamily="sans-serif" fill="var(--color-secondary)">起算条件 → 保存期間 → 処理方法 · 期間値は組織方針から転記</text>
    <text x="48" y="148" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">保存対象 / 保管先</text>
    <text x="430" y="148" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">期間の起算</text>
    <text x="720" y="148" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">保存期間</text>
    <text x="870" y="148" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">期間終了後</text>
    <text x="1030" y="148" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">保留・例外 / 根拠</text>
    {records.map((record, index) => {
      const y = 180 + index * rowHeight;
      return <g key={record.id}>
        <line x1="48" x2="1232" y1={y - 17} y2={y - 17} stroke="var(--color-outline)" />
        <text x="50" y={y + 4} fontSize="11" fontFamily="sans-serif" fill="var(--color-tertiary)">{record.id}</text>
        <text x="155" y={y + 4} fontSize="12" fontWeight="600" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(record.recordClass, 21)}</text>
        <text x="155" y={y + 20} fontSize="10" fontFamily="sans-serif" fill="var(--color-secondary)">{compact(record.system, 21)}</text>
        <text x="430" y={y + 4} fontSize="11" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(record.startEvent, 31)}</text>
        <text x="720" y={y + 5} fontSize="17" fontFamily="sans-serif" fill="var(--color-on-surface)">{record.retentionValue}{units[record.retentionUnit]}</text>
        <text x="870" y={y + 4} fontSize="12" fontFamily="sans-serif" fill="var(--color-on-surface)">{disposalNames[record.disposal]}</text>
        <text x="1030" y={y + 4} fontSize="11" fontFamily="sans-serif" fill={record.exceptionStatus === "active" ? "var(--color-tertiary)" : "var(--color-secondary)"}>{record.exceptionStatus === "active" ? compact(record.exceptionReference ?? "保留あり", 18) : "保留なし"}</text>
        <text x="1030" y={y + 20} fontSize="10" fontFamily="sans-serif" fill="var(--color-secondary)">{compact(record.retentionBasis, 18)}</text>
      </g>;
    })}
    <text x="48" y="704" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">{data.records.length}分類 · 起算条件と期間の根拠を確認</text>
  </svg>;
}

export default function Template(data: Props) {
  const exceptions = data.records.filter((record) => record.exceptionStatus === "active").length;
  return <section className="asset asset-data-retention-register">
    <style>{CSS}</style>
    <header>
      <p className="eyebrow">{data.scope} · 保存と廃棄の方針</p>
      <h1>{data.title}</h1>
      {data.subtitle && <p className="subtitle">{data.subtitle}</p>}
    </header>
    <div className="summary" aria-label="保存対象の集計">
      <p><strong>{data.records.length}</strong>保存対象</p>
      <p><strong>{exceptions}</strong>保留・例外あり</p>
      <p>期間は各組織の承認済み方針に従う</p>
    </div>
    <div>
      {data.records.map((record) => <article className="record" key={record.id}>
        <div>
          <span className="id">{record.id}</span>
          <div className="record-head">{record.recordClass}</div>
          <div className="system">{record.system}</div>
          <div className="owner">責任者: {record.owner}</div>
        </div>
        <div className="event"><span className="field-label">起算となる出来事</span>{record.startEvent}</div>
        <div><span className="field-label">保存期間</span><div className="term">{record.retentionValue} {units[record.retentionUnit]}</div></div>
        <div><span className="field-label">期間終了後</span><div className="disposal">{disposalNames[record.disposal]}</div><p className="basis">根拠: {record.retentionBasis}</p></div>
        <div className={`exception ${record.exceptionStatus === "active" ? "exception-active" : ""}`}>
          <span className="field-label">保留・例外</span>
          {record.exceptionStatus === "active" ? `あり · ${record.exceptionReference}` : "なし"}
        </div>
      </article>)}
    </div>
    <footer>期間・起算・例外条件は、組織の承認済み方針と担当者に照らして確認する。</footer>
    <Still data={data} />
  </section>;
}

schema.ts

2.0 KB

/designs/data-retention-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);

export const retentionRecordSchema = z.object({
  id: short.describe("保存対象の一意な識別子"),
  recordClass: short.describe("記録・データの分類"),
  system: short.describe("保管するシステムまたは場所"),
  owner: short.describe("管理責任者またはチーム"),
  startEvent: text.describe("保存期間の起算となる出来事"),
  retentionValue: z.number().int().positive().max(9999).describe("組織方針で定めた保存期間の値"),
  retentionUnit: z.enum(["days", "months", "years"]).describe("保存期間の単位"),
  disposal: z.enum(["delete", "anonymize", "archive"]).describe("期間終了後の処理"),
  retentionBasis: text.describe("期間を定めた組織方針・記録"),
  exceptionStatus: z.enum(["none", "active"]).describe("個別の保留・例外の有無"),
  exceptionReference: text.optional().describe("保留・例外の記録IDと見直し条件"),
}).strict();

export const schema = z.object({
  title: text.describe("台帳の目的を伝える見出し"),
  subtitle: text.optional().describe("対象範囲・基準日・方針の前提"),
  scope: short.describe("対象システム・組織・リリース"),
  records: z.array(retentionRecordSchema).min(3).max(10).describe("保存対象ごとの起算・期間・処理・根拠"),
}).strict().superRefine((data, ctx) => {
  const ids = data.records.map((record) => record.id);
  if (new Set(ids).size !== ids.length) {
    ctx.addIssue({ code: "custom", path: ["records"], message: "保存対象のIDは一意にすること" });
  }
  data.records.forEach((record, index) => {
    if (record.exceptionStatus === "active" && !record.exceptionReference) {
      ctx.addIssue({ code: "custom", path: ["records", index, "exceptionReference"], message: "保留・例外には記録IDと見直し条件が必要" });
    }
  });
});

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

meta.yaml

1.6 KB

/designs/data-retention-register/source/meta.yaml

Show contents
id: data-retention-register
name:
  ja: データ保持・廃棄台帳
  en: Data Retention and Disposal Register
purpose:
  ja: 保存対象ごとの起算条件・期間・処理方法・承認根拠と例外を記録する。
  en: Record the retention trigger, duration, disposition, approved basis, and exceptions for each data class.
status: published
still: ./still.png
category: governance
catalogOrder: 810
jobs:
  - assign-governance
useCases:
  - requirements
layoutKinds:
  - table
tags:
  - data-governance
  - retention
  - operations
  - offline
  - responsive
whenToUse:
  - ja: 承認済みの組織方針を保存対象ごとに台帳化し、起算・期間・廃棄方法・例外を確認するとき。
    en: Use to track an organization's approved retention triggers, periods, disposition, and exceptions by data class.
avoidWhen:
  - ja: 法令上の保存義務や適正な期間を判断する場合は、所管の専門家と一次資料を確認する。
    en: Do not use this register to determine legal obligations or prescribe retention periods.
requiredInputs:
  - ja: 対象分類、保管先、責任者、起算条件、承認済み期間、終了後の処理、根拠を揃える。
    en: Provide the data class, repository, owner, start event, approved duration, disposition, and policy basis.
relatedLayouts:
  - data-lineage-stages
  - permission-matrix
dataSchema:
  title: string — 台帳の目的
  subtitle: string? — 対象範囲・基準日・前提
  scope: string — 対象システム・組織
  records: array (3–10) — 保存対象、起算、期間、処理、根拠、例外
sources: []

sample-data.json

1.9 KB

/designs/data-retention-register/source/sample-data.json

Show contents
{
  "title": "保存の起点と終了後の処理を対象ごとに揃える",
  "subtitle": "架空の業務支援クラウド|社内承認済み保持方針の抜粋",
  "scope": "業務支援クラウド",
  "records": [
    {
      "id": "DATA-01",
      "recordClass": "顧客プロフィール",
      "system": "顧客データベース",
      "owner": "顧客運用チーム",
      "startEvent": "顧客アカウントの閉鎖日",
      "retentionValue": 24,
      "retentionUnit": "months",
      "disposal": "delete",
      "retentionBasis": "架空の社内記録保持基準 R-12",
      "exceptionStatus": "none"
    },
    {
      "id": "DATA-02",
      "recordClass": "問い合わせ履歴",
      "system": "サポート管理システム",
      "owner": "サポート責任者",
      "startEvent": "問い合わせの解決日",
      "retentionValue": 18,
      "retentionUnit": "months",
      "disposal": "anonymize",
      "retentionBasis": "架空のサポート運用方針 CS-4",
      "exceptionStatus": "active",
      "exceptionReference": "保留記録 HOLD-204。担当者が四半期ごとに継続要否を確認"
    },
    {
      "id": "DATA-03",
      "recordClass": "監査イベント",
      "system": "監査ログ保管庫",
      "owner": "セキュリティ運用",
      "startEvent": "イベントの記録日",
      "retentionValue": 3,
      "retentionUnit": "years",
      "disposal": "archive",
      "retentionBasis": "架空の監査ログ方針 SEC-7",
      "exceptionStatus": "none"
    },
    {
      "id": "DATA-04",
      "recordClass": "一時アップロード",
      "system": "添付ファイル領域",
      "owner": "基盤チーム",
      "startEvent": "処理ジョブの完了日",
      "retentionValue": 30,
      "retentionUnit": "days",
      "disposal": "delete",
      "retentionBasis": "架空の一時領域運用基準 OPS-3",
      "exceptionStatus": "none"
    }
  ]
}