索引に戻る

インシデント対応・エスカレーション方針

時系列
生成可能
インシデント対応・エスカレーション方針

作品として見る

持ち帰る

コピーして貼るか、ファイルに保存して渡す。ネットにつながらない環境には「全部入り」を 1 つ渡せば足りる。

全部入り

incident-escalation-policy-everything.md

このパネルの中身を、URL の先にあるファイルの中身まで含めて 1 つにまとめた Markdown

依頼文

1.6 KB
「インシデント対応・エスカレーション方針」でHTML資料を作ってください。この型は実行できるので、版面を組み直す必要はありません — 必要なのは中身です。

## この型がすること
重大度ごとの初動目標、顧客連絡、段階的な引き継ぎを事前に合意する。

## 渡す必要があるもの
- 重大度ごとの適用条件、時間目標、連絡間隔、引き継ぎ先と対応を定義する。

## 入力の形
- `title` — string — 方針の結論
- `subtitle` — string? — 適用範囲・前提・確認日
- `service` — string — 対象サービスまたは運用チーム
- `tiers` — array (3–5) — 重大度ごとの条件・応答目標・連絡間隔・引き継ぎ段階(1–4)

入力の機械可読な検証は schema.ts が持ちます。足りない入力は推測せず質問してください。

## 向かない場合
- 障害の原因や再発防止を記録する場合は、インシデントレビューを使う。

## 作り方
入力を JSON にまとめ、次を実行します。

```bash
node scripts/render.mjs incident-escalation-policy <入力.json> --theme ink -o out.html
```

出力は 1 ファイルで完結し、外部から何も読み込みません。

## 原本
- https://design-vault.pages.dev/designs/incident-escalation-policy/source/template.tsx
- https://design-vault.pages.dev/designs/incident-escalation-policy/source/schema.ts
- https://design-vault.pages.dev/designs/incident-escalation-policy/source/sample-data.json
- https://design-vault.pages.dev/designs/incident-escalation-policy/sample.html

ファイル

テンプレート・入力スキーマ・サンプルデータ・メタ情報。この文書はこの 4 つから生成されています。 生成された文書

template.tsx

10.1 KB

/designs/incident-escalation-policy/source/template.tsx

中身を見る
import type { Props } from "./schema";

const CSS = String.raw`
.asset-incident-escalation-policy{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-incident-escalation-policy *{box-sizing:border-box}.asset-incident-escalation-policy header{margin-bottom:2rem}.asset-incident-escalation-policy .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);color:var(--color-tertiary);margin-bottom:.75rem}.asset-incident-escalation-policy h1{font-size:var(--text-h1);line-height:1.3;max-width:28em}.asset-incident-escalation-policy .subtitle{margin-top:.75rem;color:var(--color-secondary)}
.asset-incident-escalation-policy .policy{border-top:2px solid var(--color-primary)}.asset-incident-escalation-policy .tier{display:grid;grid-template-columns:minmax(9rem,1fr) minmax(18rem,2fr) minmax(12rem,1.15fr) minmax(16rem,1.6fr);gap:1rem;padding:1.2rem 0;border-bottom:1px solid var(--color-outline);align-items:start}.asset-incident-escalation-policy .tier>*{min-width:0}.asset-incident-escalation-policy .priority{display:flex;gap:.7rem;align-items:baseline}.asset-incident-escalation-policy .priority b{font-family:var(--font-mono);font-size:var(--text-h2);font-variant-numeric:tabular-nums}.asset-incident-escalation-policy .priority span{font-weight:650}.asset-incident-escalation-policy .trigger{margin-top:.45rem;color:var(--color-secondary);overflow-wrap:anywhere}.asset-incident-escalation-policy .target{display:grid;grid-template-columns:1fr 1fr;gap:.6rem}.asset-incident-escalation-policy .target div{border-left:2px solid var(--color-tertiary);padding-left:.65rem}.asset-incident-escalation-policy .target dt,.asset-incident-escalation-policy .small-label{font-size:var(--text-label);color:var(--color-secondary)}.asset-incident-escalation-policy .target dd{margin:0;font-family:var(--font-mono);font-size:var(--text-h3);font-variant-numeric:tabular-nums}.asset-incident-escalation-policy .updates{margin-top:.7rem;font-size:var(--text-label)}.asset-incident-escalation-policy .steps{display:flex;flex-direction:column;gap:.5rem;list-style:none;margin:0;padding:0}.asset-incident-escalation-policy .steps li{display:grid;grid-template-columns:3.5rem minmax(0,1fr);gap:.6rem;border-left:1px solid var(--color-outline);padding-left:.7rem}.asset-incident-escalation-policy .steps time{font-family:var(--font-mono);font-variant-numeric:tabular-nums;color:var(--color-tertiary);font-weight:650;white-space:nowrap}.asset-incident-escalation-policy .steps p{margin:0;overflow-wrap:anywhere}.asset-incident-escalation-policy .steps strong{font-weight:650}.asset-incident-escalation-policy .details{margin-top:1.25rem;border-top:1px solid var(--color-outline)}.asset-incident-escalation-policy .details summary{cursor:pointer;padding:.8rem 0;min-height:2.75rem;color:var(--color-secondary)}.asset-incident-escalation-policy .details summary:focus-visible{outline:2px solid var(--color-tertiary);outline-offset:2px}.asset-incident-escalation-policy .detail{padding:1rem 0;border-top:1px solid var(--color-outline)}.asset-incident-escalation-policy .detail h3{margin:0 0 .6rem;font-size:var(--text-body-md)}.asset-incident-escalation-policy .detail p{margin:.35rem 0;overflow-wrap:anywhere}.asset-incident-escalation-policy .detail ol{margin:.6rem 0;padding-left:1.4rem}.asset-incident-escalation-policy .detail li+li{margin-top:.4rem}.asset-incident-escalation-policy footer{margin-top:1.5rem;padding-top:.75rem;border-top:1px solid var(--color-outline);color:var(--color-secondary);font-size:var(--text-label)}
.asset-incident-escalation-policy .still-svg{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media(max-width:760px){.asset-incident-escalation-policy{padding:1.5rem 1rem}.asset-incident-escalation-policy h1{font-size:var(--text-h2)}.asset-incident-escalation-policy header{margin-bottom:1.25rem}.asset-incident-escalation-policy .tier{grid-template-columns:minmax(0,1fr);gap:.8rem;padding:1rem 0}.asset-incident-escalation-policy .target{max-width:26rem}.asset-incident-escalation-policy .updates{margin-top:.4rem}.asset-incident-escalation-policy .steps{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.65rem 1rem}.asset-incident-escalation-policy .steps li{grid-template-columns:1fr;gap:.1rem}.asset-incident-escalation-policy .steps time{font-size:var(--text-label)}}
@media print{.asset-incident-escalation-policy{max-width:none;padding:1rem}.asset-incident-escalation-policy .details>*{display:block}.asset-incident-escalation-policy .details summary{display:none}}
`;

const priorityNumber = (priority: string) => Number(priority.slice(1));
const compact = (value: string, max: number) => [...value].length > max ? `${[...value].slice(0, max - 1).join("")}…` : value;

function Still({ data }: { data: Props }) {
  const sorted = [...data.tiers].sort((a, b) => priorityNumber(a.priority) - priorityNumber(b.priority));
  const rowHeight = 112;
  const firstY = 132;
  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="50" fontSize="14" fontFamily="sans-serif" fill="var(--color-secondary)">{data.service}</text>
    <text x="48" y="91" fontSize="28" fontWeight="700" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(data.title, 54)}</text>
    <text x="48" y="119" fontSize="13" fontFamily="sans-serif" fill="var(--color-secondary)">初動目標・顧客連絡・段階的な引き継ぎ</text>
    {sorted.map((tier, index) => {
      const y = firstY + index * rowHeight;
      return <g key={tier.id}>
        <line x1="48" x2="1232" y1={y - 22} y2={y - 22} stroke="var(--color-outline)" />
        <text x="52" y={y + 16} fontSize="25" fontWeight="700" fontFamily="sans-serif" fill="var(--color-tertiary)">{tier.priority}</text>
        <text x="52" y={y + 39} fontSize="15" fontWeight="600" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(tier.label, 16)}</text>
        <text x="52" y={y + 62} fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">{compact(tier.trigger, 28)}</text>
        <text x="360" y={y + 4} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">一次応答</text>
        <text x="360" y={y + 31} fontSize="22" fontFamily="sans-serif" fill="var(--color-on-surface)">{tier.acknowledgeMinutes}分</text>
        <text x="480" y={y + 4} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">影響緩和</text>
        <text x="480" y={y + 31} fontSize="22" fontFamily="sans-serif" fill="var(--color-on-surface)">{tier.mitigationTargetMinutes}分</text>
        <text x="610" y={y + 4} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">顧客連絡</text>
        <text x="610" y={y + 31} fontSize="22" fontFamily="sans-serif" fill="var(--color-on-surface)">{tier.customerUpdateEveryMinutes}分ごと</text>
        {tier.steps.map((step, stepIndex) => {
          const x = 770 + stepIndex * 112;
          return <g key={step.id}>
            <line x1={x - 14} x2={x + 92} y1={y + 20} y2={y + 20} stroke="var(--color-outline)" strokeWidth="2" />
            <circle cx={x} cy={y + 20} r="5" fill="var(--color-tertiary)" />
            <text x={x} y={y - 1} fontSize="11" fontFamily="sans-serif" fill="var(--color-tertiary)">{step.afterMinutes}分後</text>
            <text x={x} y={y + 48} fontSize="12" fontWeight="600" fontFamily="sans-serif" fill="var(--color-on-surface)">{compact(step.owner, 11)}</text>
            <text x={x} y={y + 68} fontSize="11" fontFamily="sans-serif" fill="var(--color-secondary)">{compact(step.id, 12)}</text>
          </g>;
        })}
      </g>;
    })}
    <text x="48" y="704" fontSize="12" fontFamily="sans-serif" fill="var(--color-secondary)">適用前に当番・連絡先・時刻の起点を確認する</text>
  </svg>;
}

export default function Template(data: Props) {
  const tiers = [...data.tiers].sort((a, b) => priorityNumber(a.priority) - priorityNumber(b.priority));
  return <section className="asset asset-incident-escalation-policy">
    <style>{CSS}</style>
    <header>
      <p className="eyebrow">{data.service} の対応方針</p>
      <h1>{data.title}</h1>
      {data.subtitle && <p className="subtitle">{data.subtitle}</p>}
    </header>
    <div className="policy" aria-label="重大度ごとの初動とエスカレーション">
      {tiers.map((tier) => <article className="tier" data-tier-id={tier.id} key={tier.id}>
        <div>
          <div className="priority"><b>{tier.priority}</b><span>{tier.label}</span></div>
          <p className="trigger">{tier.trigger}</p>
        </div>
        <dl className="target">
          <div><dt>一次応答まで</dt><dd>{tier.acknowledgeMinutes} 分</dd></div>
          <div><dt>影響緩和の目標</dt><dd>{tier.mitigationTargetMinutes} 分</dd></div>
          <div className="updates"><dt>顧客への状況連絡</dt><dd>{tier.customerUpdateEveryMinutes} 分ごと</dd></div>
        </dl>
        <div className="small-label">経過時間による引き継ぎ</div>
        <ol className="steps">{tier.steps.map((step) => <li key={step.id}>
          <time>{step.afterMinutes}分後</time>
          <p><strong>{step.owner}</strong><br />{step.action}</p>
        </li>)}</ol>
      </article>)}
    </div>
    <details className="details">
      <summary>対応条件をまとめて確認</summary>
      {tiers.map((tier) => <section className="detail" key={tier.id}>
        <h3>{tier.priority} · {tier.label}</h3>
        <p><strong>適用条件:</strong> {tier.trigger}</p>
        <p><strong>初動:</strong> {tier.acknowledgeMinutes}分以内に応答、{tier.mitigationTargetMinutes}分以内に影響を緩和。顧客へ{tier.customerUpdateEveryMinutes}分ごとに状況を連絡。</p>
        <ol>{tier.steps.map((step) => <li key={step.id}>{step.afterMinutes}分後 — {step.owner}: {step.action}</li>)}</ol>
      </section>)}
    </details>
    <footer>通知先と経過時間の起点を運用手順に明記し、定期的に見直す。</footer>
    <Still data={data} />
  </section>;
}

schema.ts

2.7 KB

/designs/incident-escalation-policy/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);
const minutes = z.number().int().positive();

const escalationStepSchema = z.object({
  id: short.describe("この段階の識別子"),
  afterMinutes: minutes.describe("障害検知からの経過時間(分)"),
  owner: short.describe("次に対応を引き継ぐ役割"),
  action: text.describe("引き継ぎ時に行う確認・判断"),
}).strict();

export const incidentTierSchema = z.object({
  id: short.describe("重大度行の一意な識別子"),
  priority: z.enum(["P0", "P1", "P2", "P3", "P4"]).describe("P0が最優先"),
  label: short.describe("重大度の短い名称"),
  trigger: text.describe("この重大度を選ぶ状況"),
  acknowledgeMinutes: minutes.describe("一次応答までの目標時間"),
  mitigationTargetMinutes: minutes.describe("影響を抑えるまでの目標時間"),
  customerUpdateEveryMinutes: minutes.describe("顧客への状況連絡間隔"),
  steps: z.array(escalationStepSchema).min(1).max(4).describe("経過時間ごとの段階的な引き継ぎ"),
}).strict();

export const schema = z.object({
  title: text.describe("対応方針の要点"),
  subtitle: text.optional().describe("適用サービス・前提・確認日"),
  service: short.describe("対象サービスまたは運用チーム"),
  tiers: z.array(incidentTierSchema).min(3).max(5).describe("重大度ごとの初動とエスカレーション方針"),
}).strict().superRefine((data, ctx) => {
  const ids = data.tiers.map((tier) => tier.id);
  if (new Set(ids).size !== ids.length) {
    ctx.addIssue({ code: "custom", path: ["tiers"], message: "重大度行のidは一意にすること" });
  }
  const priorities = data.tiers.map((tier) => tier.priority);
  if (new Set(priorities).size !== priorities.length) {
    ctx.addIssue({ code: "custom", path: ["tiers"], message: "重大度は重複できない" });
  }
  if (priorities.some((priority, index) => index > 0 && priority < priorities[index - 1])) {
    ctx.addIssue({ code: "custom", path: ["tiers"], message: "重大度はP0から優先順に並べること" });
  }
  data.tiers.forEach((tier, tierIndex) => {
    const stepIds = tier.steps.map((step) => step.id);
    if (new Set(stepIds).size !== stepIds.length) {
      ctx.addIssue({ code: "custom", path: ["tiers", tierIndex, "steps"], message: "引き継ぎ段階のidは一意にすること" });
    }
    if (tier.steps.some((step, index) => index > 0 && step.afterMinutes <= tier.steps[index - 1].afterMinutes)) {
      ctx.addIssue({ code: "custom", path: ["tiers", tierIndex, "steps"], message: "引き継ぎ時刻は昇順で、一意にすること" });
    }
  });
});

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

meta.yaml

1.6 KB

/designs/incident-escalation-policy/source/meta.yaml

中身を見る
id: incident-escalation-policy
name:
  ja: インシデント対応・エスカレーション方針
  en: Incident Response and Escalation Policy
purpose:
  ja: 重大度ごとの初動目標、顧客連絡、段階的な引き継ぎを事前に合意する。
  en: Set response targets, customer updates, and escalation steps for each incident priority.
status: published
still: ./still.png
category: operations
catalogOrder: 790
jobs:
  - plan-sequence
  - assign-governance
useCases:
  - process
layoutKinds:
  - timeline
  - table
tags:
  - incident-response
  - escalation
  - operations
  - offline
  - responsive
whenToUse:
  - ja: 障害の重大度と経過時間に応じた応答目標、連絡間隔、引き継ぎ先を合意するとき。
    en: Use when agreeing on response targets, update cadence, and handoffs by incident priority.
avoidWhen:
  - ja: 障害の原因や再発防止を記録する場合は、インシデントレビューを使う。
    en: Use an incident review to record causes and prevention after an incident.
requiredInputs:
  - ja: 重大度ごとの適用条件、時間目標、連絡間隔、引き継ぎ先と対応を定義する。
    en: Define each priority trigger, time target, update cadence, escalation owner, and action.
relatedLayouts:
  - incident-review
  - release-readiness
dataSchema:
  title: string — 方針の結論
  subtitle: string? — 適用範囲・前提・確認日
  service: string — 対象サービスまたは運用チーム
  tiers: array (3–5) — 重大度ごとの条件・応答目標・連絡間隔・引き継ぎ段階(1–4)
sources: []

sample-data.json

3.1 KB

/designs/incident-escalation-policy/source/sample-data.json

中身を見る
{
  "title": "重大度に応じた初動と引き継ぎを揃える",
  "subtitle": "架空のSaaS運用方針|検知から経過した時間で引き継ぐ",
  "service": "業務支援クラウド",
  "tiers": [
    {
      "id": "tier-p0",
      "priority": "P0",
      "label": "全体停止",
      "trigger": "全利用者が主要操作を行えず、回避策もない",
      "acknowledgeMinutes": 5,
      "mitigationTargetMinutes": 30,
      "customerUpdateEveryMinutes": 15,
      "steps": [
        {
          "id": "p0-step-1",
          "afterMinutes": 10,
          "owner": "当番責任者",
          "action": "指揮役を決め、復旧担当を招集する"
        },
        {
          "id": "p0-step-2",
          "afterMinutes": 20,
          "owner": "サービス責任者",
          "action": "回避策と復旧見込みを判断する"
        },
        {
          "id": "p0-step-3",
          "afterMinutes": 40,
          "owner": "事業責任者",
          "action": "広域連絡と外部支援の要否を決める"
        }
      ]
    },
    {
      "id": "tier-p1",
      "priority": "P1",
      "label": "主要機能の障害",
      "trigger": "一部の利用者で主要機能が使えず、影響が拡大している",
      "acknowledgeMinutes": 15,
      "mitigationTargetMinutes": 90,
      "customerUpdateEveryMinutes": 30,
      "steps": [
        {
          "id": "p1-step-1",
          "afterMinutes": 30,
          "owner": "当番責任者",
          "action": "影響範囲と直近の変更を照合する"
        },
        {
          "id": "p1-step-2",
          "afterMinutes": 60,
          "owner": "サービス責任者",
          "action": "追加要員または機能停止を判断する"
        }
      ]
    },
    {
      "id": "tier-p2",
      "priority": "P2",
      "label": "機能劣化",
      "trigger": "応答遅延または限定した機能不全が続く",
      "acknowledgeMinutes": 60,
      "mitigationTargetMinutes": 480,
      "customerUpdateEveryMinutes": 120,
      "steps": [
        {
          "id": "p2-step-1",
          "afterMinutes": 120,
          "owner": "担当チーム",
          "action": "再現条件と影響を記録する"
        },
        {
          "id": "p2-step-2",
          "afterMinutes": 240,
          "owner": "当番責任者",
          "action": "当日中の修正可否を確認する"
        }
      ]
    },
    {
      "id": "tier-p3",
      "priority": "P3",
      "label": "軽微な不具合",
      "trigger": "限定した利用者に影響し、回避策が利用できる",
      "acknowledgeMinutes": 240,
      "mitigationTargetMinutes": 2880,
      "customerUpdateEveryMinutes": 1440,
      "steps": [
        {
          "id": "p3-step-1",
          "afterMinutes": 480,
          "owner": "担当チーム",
          "action": "通常の計画へ登録し対応時期を共有する"
        },
        {
          "id": "p3-step-2",
          "afterMinutes": 1440,
          "owner": "プロダクト責任者",
          "action": "未対応理由と次回確認日を合意する"
        }
      ]
    }
  ]
}