作品として見る 生成された文書そのもの — 切り替えも開閉もそのまま動きます
作品として見る 生成された文書そのもの — 切り替えも開閉もそのまま動きます
コピーして貼るか、ファイルに保存して渡す。ネットにつながらない環境には「全部入り」を 1 つ渡せば足りる。
このパネルの中身を、URL の先にあるファイルの中身まで含めて 1 つにまとめた Markdown — 大きいのでファイルで渡す(コピーは 32KB まで)
「データ保持・廃棄台帳」でHTML資料を作ってください。この型は実行できるので、版面を組み直す必要はありません — 必要なのは中身です。 ## この型がすること 保存対象ごとの起算条件・期間・処理方法・承認根拠と例外を記録する。 ## 渡す必要があるもの - 対象分類、保管先、責任者、起算条件、承認済み期間、終了後の処理、根拠を揃える。 ## 入力の形 - `title` — string — 台帳の目的 - `subtitle` — string? — 対象範囲・基準日・前提 - `scope` — string — 対象システム・組織 - `records` — array (3–10) — 保存対象、起算、期間、処理、根拠、例外 入力の機械可読な検証は schema.ts が持ちます。足りない入力は推測せず質問してください。 ## 向かない場合 - 法令上の保存義務や適正な期間を判断する場合は、所管の専門家と一次資料を確認する。 ## 作り方 入力を JSON にまとめ、次を実行します。 ```bash node scripts/render.mjs data-retention-register <入力.json> --theme ink -o out.html ``` 出力は 1 ファイルで完結し、外部から何も読み込みません。 ## 原本 - 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
テンプレート・入力スキーマ・サンプルデータ・メタ情報。この文書はこの 4 つから生成されています。 生成された文書
/designs/data-retention-register/source/template.tsx
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>;
}
/designs/data-retention-register/source/schema.ts
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>;
/designs/data-retention-register/source/meta.yaml
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: []
/designs/data-retention-register/source/sample-data.json
{
"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"
}
]
}